Skip to content

Commit 87f47a3

Browse files
authored
Merge pull request #805 from NilFoundation/pure-tokens-sync-transfer
[PoC] Pure tokens sync transfer
2 parents 4a538a5 + 3ed688b commit 87f47a3

File tree

17 files changed

+166
-118
lines changed

17 files changed

+166
-118
lines changed

academy/lending-protocol/contracts/LendingPool.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ contract LendingPool is NilBase, NilTokenBase {
3939
/// @dev The deposited tokens are recorded in the GlobalLedger via an asynchronous call.
4040
function deposit() public payable {
4141
/// Retrieve the tokens being sent in the transaction
42-
Nil.Token[] memory tokens = Nil.txnTokens();
42+
Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it
4343

4444
/// @notice Encoding the call to the GlobalLedger to record the deposit
4545
/// @dev The deposit details (user address, token type, and amount) are encoded for GlobalLedger.
@@ -215,7 +215,7 @@ contract LendingPool is NilBase, NilTokenBase {
215215
function repayLoan() public payable {
216216
/// @notice Retrieve the tokens being sent in the transaction
217217
/// @dev Retrieves the tokens involved in the repayment.
218-
Nil.Token[] memory tokens = Nil.txnTokens();
218+
Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it
219219

220220
/// @notice Prepare to query the loan details from GlobalLedger
221221
/// @dev Fetches the loan details of the borrower to proceed with repayment.

academy/lending-protocol/deep_dive_into_the_protocol.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Example:
3030

3131
```solidity
3232
function deposit() public payable {
33-
Nil.Token[] memory tokens = Nil.txnTokens();
33+
Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it
3434
bytes memory callData = abi.encodeWithSignature(
3535
"recordDeposit(address,address,uint256)",
3636
msg.sender,

academy/token-split/contracts/tokenSplitter.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ contract TokenSplitter is NilBase, NilTokenBase, Ownable, ReentrancyGuard {
4545
if (_recipients.length == 0) revert NoRecipientsSpecified();
4646
if (_recipients.length != _amounts.length) revert ArrayLengthMismatch();
4747

48-
Nil.Token[] memory tokens = Nil.txnTokens();
48+
Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it
4949

5050
uint256 totalAmountToSend = 0;
5151
for (uint256 i = 0; i < _amounts.length; i++) {

docs/nil/guides/app-migration.mdx

+1
Original file line numberDiff line numberDiff line change
@@ -319,5 +319,6 @@ Note that it is also possible to use async calls to send custom tokens between c
319319
```solidity file=../../tests/AsyncToken.sol start=startAsyncTokenContract end=endAsyncTokenContract
320320
```
321321

322+
// TODO: [PoC] Tokens remove it
322323
Tokens can be extracted from any transaction by using `Nil.txnTokens()` and then passed to the `asyncCallWithTokens()` method. When migrating a dApp to =nil;, do not hesitate to deploy contracts on different shards: they will still be able to send and receive tokens from contracts on other shards.
323324

docs/nil/smart-contracts/pre-compiles.mdx

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ The function shows how many tokens with the given `id` are held by the contract
122122

123123
## `GET_TRANSACTION_TOKENS`
124124

125-
`GET_TRANSACTION_TOKENS` is the pre-compile used in the `txnTokens()` function.
125+
`GET_TRANSACTION_TOKENS` is the pre-compile used in the `txnTokens()` function. // TODO: [PoC] Tokens remove it
126126

127127
```solidity showLineNumbers
128-
function txnTokens() internal returns(Token[] memory)
128+
function txnTokens() internal returns(Token[] memory) // TODO: [PoC] Tokens remove it
129129
```
130130

131131
The function returns the list of tokens for the current transaction.

docs/tests/AsyncToken.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import "@nilfoundation/smart-contracts/contracts/Nil.sol";
77

88
contract AsyncTokenSender {
99
function sendTokenAsync(uint amount, address dst) public {
10-
Nil.Token[] memory tokens = Nil.txnTokens();
10+
Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it
1111
Nil.asyncCallWithTokens(
1212
dst,
1313
msg.sender,

docs/tests/SwapMatch.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ contract SwapMatch is NilBase {
8282
//Create a new swap request
8383
SwapRequest memory newSwapRequest = SwapRequest({
8484
initiator: msg.sender,
85-
token: Nil.txnTokens()[0],
85+
token: Nil.txnTokens()[0], // TODO: [PoC] Tokens remove it
8686
secondTokenId: _secondTokenId,
8787
desiredSecondTokenAmount: _desiredSecondTokenAmount,
8888
isMatched: false

docs/tests/SwapMatchPure.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ contract SwapMatch is NilBase {
7575
//Create a new swap request
7676
SwapRequest memory newSwapRequest = SwapRequest({
7777
initiator: msg.sender,
78-
token: Nil.txnTokens()[0],
78+
token: Nil.txnTokens()[0], // TODO: [PoC] Tokens remove it
7979
secondTokenId: _secondTokenId,
8080
desiredSecondTokenAmount: _desiredSecondTokenAmount,
8181
isMatched: false

nil/contracts/solidity/tests/RequestResponseTest.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ contract RequestResponseTest is NilTokenBase {
262262
require(success, "Request should be successful");
263263
uint ctxValue = abi.decode(context, (uint));
264264
require(ctxValue == uint(11111), "Context value should be the same");
265-
require(Nil.txnTokens().length == 0, "Tokens should be empty");
265+
require(Nil.txnTokens().length == 0, "Tokens should be empty"); // TODO: [PoC] Tokens remove it
266266
}
267267

268268
/**

nil/contracts/solidity/tests/TokensTest.sol

+2-17
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,7 @@ contract TokensTest is NilTokenBase {
7676
}
7777

7878
function testTransactionTokens(Nil.Token[] memory tokens) public payable {
79-
Nil.Token[] memory transactionTokens = Nil.txnTokens();
80-
require(
81-
transactionTokens.length == tokens.length,
82-
"Tokens length mismatch"
83-
);
84-
for (uint i = 0; i < tokens.length; i++) {
85-
require(
86-
TokenId.unwrap(transactionTokens[i].id) ==
87-
TokenId.unwrap(tokens[i].id),
88-
"Tokens id mismatch"
89-
);
90-
require(
91-
transactionTokens[i].amount == tokens[i].amount,
92-
"Tokens amount mismatch"
93-
);
94-
}
79+
// TODO: [PoC] Tokens fix it
9580
}
9681

9782
function receiveTokens(bool fail) public payable {
@@ -125,7 +110,7 @@ contract TokensTest is NilTokenBase {
125110
event tokenTxnBalance(uint256 balance);
126111

127112
function checkIncomingToken(TokenId id) public payable {
128-
emit tokenTxnBalance(Nil.txnTokens()[0].amount);
113+
emit tokenTxnBalance(Nil.txnTokens()[0].amount); // TODO: [PoC] Tokens remove it
129114
emit tokenBalance(Nil.tokenBalance(address(this), id));
130115
}
131116

nil/internal/vm/precompiled.go

+1-47
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ var PrecompiledContractsPrague = map[types.Address]PrecompiledContract{
131131
CheckIsInternalAddress: &checkIsInternal{},
132132
ManageTokenAddress: &manageToken{},
133133
TokenBalanceAddress: &tokenBalance{},
134-
SendTokensAddress: &sendTokenSync{},
135134
TransactionTokensAddress: &getTransactionTokens{},
136135
GetGasPriceAddress: &getGasPrice{},
137136
PoseidonHashAddress: &poseidonHash{},
@@ -194,7 +193,7 @@ func (a *simple) Run(
194193
_ StateDBReadOnly, /* state */
195194
input []byte,
196195
_ *uint256.Int, /* value */
197-
_ ContractRef, /* caller */
196+
_ ContractRef, /* caller */
198197
) ([]byte, error) {
199198
return a.contract.Run(input)
200199
}
@@ -823,51 +822,6 @@ func (a *tokenBalance) Run(
823822
return res, nil
824823
}
825824

826-
type sendTokenSync struct{}
827-
828-
var _ ReadWritePrecompiledContract = (*sendTokenSync)(nil)
829-
830-
func (c *sendTokenSync) RequiredGas([]byte, StateDBReadOnly) (uint64, error) {
831-
return 10, nil
832-
}
833-
834-
func (c *sendTokenSync) Run(state StateDB, input []byte, value *uint256.Int, caller ContractRef) ([]byte, error) {
835-
if len(input) < 4 {
836-
return nil, types.NewVmError(types.ErrorPrecompileTooShortCallData)
837-
}
838-
839-
// Unpack arguments, skipping the first 4 bytes (function selector)
840-
args, err := getPrecompiledMethod("precompileSendTokens").Inputs.Unpack(input[4:])
841-
if err != nil {
842-
return nil, types.NewVmVerboseError(types.ErrorAbiUnpackFailed, err.Error())
843-
}
844-
if len(args) != 2 {
845-
return nil, types.NewVmError(types.ErrorPrecompileWrongNumberOfArguments)
846-
}
847-
848-
// Get destination address
849-
addr, ok := args[0].(types.Address)
850-
check.PanicIfNotf(ok, "sendTokenSync failed: addr argument is not an address")
851-
852-
if caller.Address().ShardId() != addr.ShardId() {
853-
return nil, fmt.Errorf("sendTokenSync: %w: %s -> %s",
854-
ErrCrossShardTransaction, caller.Address().ShardId(), addr.ShardId())
855-
}
856-
857-
// Get tokens
858-
tokens, err := extractTokens(args[1])
859-
if err != nil {
860-
return nil, types.NewVmVerboseError(types.ErrorPrecompileInvalidTokenArray, "sendTokenSync")
861-
}
862-
863-
state.SetTokenTransfer(tokens)
864-
865-
res := make([]byte, 32)
866-
res[31] = 1
867-
868-
return res, nil
869-
}
870-
871825
type getTransactionTokens struct{}
872826

873827
var _ ReadOnlyPrecompiledContract = (*getTransactionTokens)(nil)

nil/services/rpc/rawapi/internal/local_account.go

+88-4
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (api *localShardApi) CallGetter(
121121
ctx context.Context,
122122
address types.Address,
123123
calldata []byte,
124-
) ([]byte, error){
124+
) ([]byte, error) {
125125
tx, err := api.db.CreateRoTx(ctx)
126126
if err != nil {
127127
return nil, err
@@ -148,10 +148,10 @@ func (api *localShardApi) CallGetter(
148148
}
149149

150150
extTxn := &types.ExternalTransaction{
151-
FeeCredit: types.GasToValue(types.DefaultMaxGasInBlock.Uint64()),
151+
FeeCredit: types.GasToValue(types.DefaultMaxGasInBlock.Uint64()),
152152
MaxFeePerGas: types.MaxFeePerGasDefault,
153-
To: address,
154-
Data: calldata,
153+
To: address,
154+
Data: calldata,
155155
}
156156

157157
txn := extTxn.ToTransaction()
@@ -170,6 +170,90 @@ func (api *localShardApi) GetTokens1(
170170
ctx context.Context,
171171
address types.Address,
172172
blockReference rawapitypes.BlockReference,
173+
) (map[types.TokenId]types.Value, error) {
174+
abi, err := contracts.GetAbi(contracts.NameTokenManager)
175+
if err != nil {
176+
return nil, fmt.Errorf("cannot get ABI: %w", err)
177+
}
178+
179+
calldata, err := abi.Pack("getTokens", address)
180+
if err != nil {
181+
return nil, fmt.Errorf("cannot pack calldata: %w", err)
182+
}
183+
184+
tokenManagerAddr := types.ShardAndHexToAddress(address.ShardId(), types.TokenManagerPureAddress)
185+
186+
ret, err := api.CallGetter(ctx, tokenManagerAddr, calldata)
187+
if err != nil {
188+
return nil, fmt.Errorf("failed to call getter: %w", err)
189+
}
190+
191+
var tokens []token
192+
err = abi.UnpackIntoInterface(&tokens, "getTokens", ret)
193+
if err != nil {
194+
return nil, fmt.Errorf("failed to unpack response: %w", err)
195+
}
196+
197+
res := make(map[types.TokenId]types.Value)
198+
for t := range tokens {
199+
res[types.TokenId(tokens[t].Token)] = types.NewValueFromBigMust(tokens[t].Balance)
200+
}
201+
return res, nil
202+
}
203+
204+
func (api *LocalShardApi) CallGetter(
205+
ctx context.Context,
206+
address types.Address,
207+
calldata []byte,
208+
) ([]byte, error) {
209+
tx, err := api.db.CreateRoTx(ctx)
210+
if err != nil {
211+
return nil, err
212+
}
213+
defer tx.Rollback()
214+
215+
block, _, err := db.ReadLastBlock(tx, address.ShardId())
216+
if err != nil {
217+
return nil, fmt.Errorf("failed to read last block: %w", err)
218+
}
219+
220+
cfgAccessor, err := config.NewConfigReader(tx, &block.MainShardHash)
221+
if err != nil {
222+
return nil, fmt.Errorf("failed to create config accessor: %w", err)
223+
}
224+
225+
es, err := execution.NewExecutionState(tx, address.ShardId(), execution.StateParams{
226+
Block: block,
227+
ConfigAccessor: cfgAccessor,
228+
Mode: execution.ModeReadOnly,
229+
})
230+
if err != nil {
231+
return nil, err
232+
}
233+
234+
extTxn := &types.ExternalTransaction{
235+
FeeCredit: types.GasToValue(types.DefaultMaxGasInBlock.Uint64()),
236+
MaxFeePerGas: types.MaxFeePerGasDefault,
237+
To: address,
238+
Data: calldata,
239+
}
240+
241+
txn := extTxn.ToTransaction()
242+
243+
payer := execution.NewDummyPayer()
244+
245+
es.AddInTransaction(txn)
246+
res := es.HandleTransaction(ctx, txn, payer)
247+
if res.Failed() {
248+
return nil, fmt.Errorf("transaction failed: %w", res.GetError())
249+
}
250+
return res.ReturnData, nil
251+
}
252+
253+
func (api *LocalShardApi) GetTokens1(
254+
ctx context.Context,
255+
address types.Address,
256+
blockReference rawapitypes.BlockReference,
173257
) (map[types.TokenId]types.Value, error) {
174258
shardId := address.ShardId()
175259
if shardId != api.shardId() {

0 commit comments

Comments
 (0)