diff --git a/academy/lending-protocol/contracts/LendingPool.sol b/academy/lending-protocol/contracts/LendingPool.sol index 6858a757b..e29f8ae0b 100644 --- a/academy/lending-protocol/contracts/LendingPool.sol +++ b/academy/lending-protocol/contracts/LendingPool.sol @@ -39,7 +39,7 @@ contract LendingPool is NilBase, NilTokenBase { /// @dev The deposited tokens are recorded in the GlobalLedger via an asynchronous call. function deposit() public payable { /// Retrieve the tokens being sent in the transaction - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it /// @notice Encoding the call to the GlobalLedger to record the deposit /// @dev The deposit details (user address, token type, and amount) are encoded for GlobalLedger. @@ -215,7 +215,7 @@ contract LendingPool is NilBase, NilTokenBase { function repayLoan() public payable { /// @notice Retrieve the tokens being sent in the transaction /// @dev Retrieves the tokens involved in the repayment. - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it /// @notice Prepare to query the loan details from GlobalLedger /// @dev Fetches the loan details of the borrower to proceed with repayment. diff --git a/academy/lending-protocol/deep_dive_into_the_protocol.md b/academy/lending-protocol/deep_dive_into_the_protocol.md index f936bb549..35bb88077 100644 --- a/academy/lending-protocol/deep_dive_into_the_protocol.md +++ b/academy/lending-protocol/deep_dive_into_the_protocol.md @@ -30,7 +30,7 @@ Example: ```solidity function deposit() public payable { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it bytes memory callData = abi.encodeWithSignature( "recordDeposit(address,address,uint256)", msg.sender, diff --git a/academy/token-split/contracts/tokenSplitter.sol b/academy/token-split/contracts/tokenSplitter.sol index 6463097f8..a9e304654 100644 --- a/academy/token-split/contracts/tokenSplitter.sol +++ b/academy/token-split/contracts/tokenSplitter.sol @@ -45,7 +45,7 @@ contract TokenSplitter is NilBase, NilTokenBase, Ownable, ReentrancyGuard { if (_recipients.length == 0) revert NoRecipientsSpecified(); if (_recipients.length != _amounts.length) revert ArrayLengthMismatch(); - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it uint256 totalAmountToSend = 0; for (uint256 i = 0; i < _amounts.length; i++) { diff --git a/docs/nil/guides/app-migration.mdx b/docs/nil/guides/app-migration.mdx index 71a380b74..8e89fb1eb 100644 --- a/docs/nil/guides/app-migration.mdx +++ b/docs/nil/guides/app-migration.mdx @@ -319,5 +319,6 @@ Note that it is also possible to use async calls to send custom tokens between c ```solidity file=../../tests/AsyncToken.sol start=startAsyncTokenContract end=endAsyncTokenContract ``` + // TODO: [PoC] Tokens remove it 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. diff --git a/docs/nil/smart-contracts/pre-compiles.mdx b/docs/nil/smart-contracts/pre-compiles.mdx index 3802cbc13..ac5a4e508 100644 --- a/docs/nil/smart-contracts/pre-compiles.mdx +++ b/docs/nil/smart-contracts/pre-compiles.mdx @@ -122,10 +122,10 @@ The function shows how many tokens with the given `id` are held by the contract ## `GET_TRANSACTION_TOKENS` -`GET_TRANSACTION_TOKENS` is the pre-compile used in the `txnTokens()` function. +`GET_TRANSACTION_TOKENS` is the pre-compile used in the `txnTokens()` function. // TODO: [PoC] Tokens remove it ```solidity showLineNumbers -function txnTokens() internal returns(Token[] memory) +function txnTokens() internal returns(Token[] memory) // TODO: [PoC] Tokens remove it ``` The function returns the list of tokens for the current transaction. diff --git a/docs/tests/AsyncToken.sol b/docs/tests/AsyncToken.sol index 6ed4f383b..9198cd5a9 100644 --- a/docs/tests/AsyncToken.sol +++ b/docs/tests/AsyncToken.sol @@ -7,7 +7,7 @@ import "@nilfoundation/smart-contracts/contracts/Nil.sol"; contract AsyncTokenSender { function sendTokenAsync(uint amount, address dst) public { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it Nil.asyncCallWithTokens( dst, msg.sender, diff --git a/docs/tests/SwapMatch.sol b/docs/tests/SwapMatch.sol index 464c1c8d1..5a03889bf 100644 --- a/docs/tests/SwapMatch.sol +++ b/docs/tests/SwapMatch.sol @@ -82,7 +82,7 @@ contract SwapMatch is NilBase { //Create a new swap request SwapRequest memory newSwapRequest = SwapRequest({ initiator: msg.sender, - token: Nil.txnTokens()[0], + token: Nil.txnTokens()[0], // TODO: [PoC] Tokens remove it secondTokenId: _secondTokenId, desiredSecondTokenAmount: _desiredSecondTokenAmount, isMatched: false diff --git a/docs/tests/SwapMatchPure.sol b/docs/tests/SwapMatchPure.sol index e0334ac83..62234cb53 100644 --- a/docs/tests/SwapMatchPure.sol +++ b/docs/tests/SwapMatchPure.sol @@ -75,7 +75,7 @@ contract SwapMatch is NilBase { //Create a new swap request SwapRequest memory newSwapRequest = SwapRequest({ initiator: msg.sender, - token: Nil.txnTokens()[0], + token: Nil.txnTokens()[0], // TODO: [PoC] Tokens remove it secondTokenId: _secondTokenId, desiredSecondTokenAmount: _desiredSecondTokenAmount, isMatched: false diff --git a/nil/contracts/solidity/tests/RequestResponseTest.sol b/nil/contracts/solidity/tests/RequestResponseTest.sol index 0e0960d70..4a0d68559 100644 --- a/nil/contracts/solidity/tests/RequestResponseTest.sol +++ b/nil/contracts/solidity/tests/RequestResponseTest.sol @@ -262,7 +262,7 @@ contract RequestResponseTest is NilTokenBase { require(success, "Request should be successful"); uint ctxValue = abi.decode(context, (uint)); require(ctxValue == uint(11111), "Context value should be the same"); - require(Nil.txnTokens().length == 0, "Tokens should be empty"); + require(Nil.txnTokens().length == 0, "Tokens should be empty"); // TODO: [PoC] Tokens remove it } /** diff --git a/nil/contracts/solidity/tests/TokensTest.sol b/nil/contracts/solidity/tests/TokensTest.sol index 3e5da728c..7d7298173 100644 --- a/nil/contracts/solidity/tests/TokensTest.sol +++ b/nil/contracts/solidity/tests/TokensTest.sol @@ -76,22 +76,7 @@ contract TokensTest is NilTokenBase { } function testTransactionTokens(Nil.Token[] memory tokens) public payable { - Nil.Token[] memory transactionTokens = Nil.txnTokens(); - require( - transactionTokens.length == tokens.length, - "Tokens length mismatch" - ); - for (uint i = 0; i < tokens.length; i++) { - require( - TokenId.unwrap(transactionTokens[i].id) == - TokenId.unwrap(tokens[i].id), - "Tokens id mismatch" - ); - require( - transactionTokens[i].amount == tokens[i].amount, - "Tokens amount mismatch" - ); - } + // TODO: [PoC] Tokens fix it } function receiveTokens(bool fail) public payable { @@ -125,7 +110,7 @@ contract TokensTest is NilTokenBase { event tokenTxnBalance(uint256 balance); function checkIncomingToken(TokenId id) public payable { - emit tokenTxnBalance(Nil.txnTokens()[0].amount); + emit tokenTxnBalance(Nil.txnTokens()[0].amount); // TODO: [PoC] Tokens remove it emit tokenBalance(Nil.tokenBalance(address(this), id)); } diff --git a/nil/internal/vm/precompiled.go b/nil/internal/vm/precompiled.go index 51173d298..6010cfba0 100644 --- a/nil/internal/vm/precompiled.go +++ b/nil/internal/vm/precompiled.go @@ -131,7 +131,6 @@ var PrecompiledContractsPrague = map[types.Address]PrecompiledContract{ CheckIsInternalAddress: &checkIsInternal{}, ManageTokenAddress: &manageToken{}, TokenBalanceAddress: &tokenBalance{}, - SendTokensAddress: &sendTokenSync{}, TransactionTokensAddress: &getTransactionTokens{}, GetGasPriceAddress: &getGasPrice{}, PoseidonHashAddress: &poseidonHash{}, @@ -194,7 +193,7 @@ func (a *simple) Run( _ StateDBReadOnly, /* state */ input []byte, _ *uint256.Int, /* value */ - _ ContractRef, /* caller */ + _ ContractRef, /* caller */ ) ([]byte, error) { return a.contract.Run(input) } @@ -823,51 +822,6 @@ func (a *tokenBalance) Run( return res, nil } -type sendTokenSync struct{} - -var _ ReadWritePrecompiledContract = (*sendTokenSync)(nil) - -func (c *sendTokenSync) RequiredGas([]byte, StateDBReadOnly) (uint64, error) { - return 10, nil -} - -func (c *sendTokenSync) Run(state StateDB, input []byte, value *uint256.Int, caller ContractRef) ([]byte, error) { - if len(input) < 4 { - return nil, types.NewVmError(types.ErrorPrecompileTooShortCallData) - } - - // Unpack arguments, skipping the first 4 bytes (function selector) - args, err := getPrecompiledMethod("precompileSendTokens").Inputs.Unpack(input[4:]) - if err != nil { - return nil, types.NewVmVerboseError(types.ErrorAbiUnpackFailed, err.Error()) - } - if len(args) != 2 { - return nil, types.NewVmError(types.ErrorPrecompileWrongNumberOfArguments) - } - - // Get destination address - addr, ok := args[0].(types.Address) - check.PanicIfNotf(ok, "sendTokenSync failed: addr argument is not an address") - - if caller.Address().ShardId() != addr.ShardId() { - return nil, fmt.Errorf("sendTokenSync: %w: %s -> %s", - ErrCrossShardTransaction, caller.Address().ShardId(), addr.ShardId()) - } - - // Get tokens - tokens, err := extractTokens(args[1]) - if err != nil { - return nil, types.NewVmVerboseError(types.ErrorPrecompileInvalidTokenArray, "sendTokenSync") - } - - state.SetTokenTransfer(tokens) - - res := make([]byte, 32) - res[31] = 1 - - return res, nil -} - type getTransactionTokens struct{} var _ ReadOnlyPrecompiledContract = (*getTransactionTokens)(nil) diff --git a/nil/services/rpc/rawapi/internal/local_account.go b/nil/services/rpc/rawapi/internal/local_account.go index 6233c3624..f55aa90ca 100644 --- a/nil/services/rpc/rawapi/internal/local_account.go +++ b/nil/services/rpc/rawapi/internal/local_account.go @@ -121,7 +121,7 @@ func (api *localShardApi) CallGetter( ctx context.Context, address types.Address, calldata []byte, -) ([]byte, error){ +) ([]byte, error) { tx, err := api.db.CreateRoTx(ctx) if err != nil { return nil, err @@ -148,10 +148,10 @@ func (api *localShardApi) CallGetter( } extTxn := &types.ExternalTransaction{ - FeeCredit: types.GasToValue(types.DefaultMaxGasInBlock.Uint64()), + FeeCredit: types.GasToValue(types.DefaultMaxGasInBlock.Uint64()), MaxFeePerGas: types.MaxFeePerGasDefault, - To: address, - Data: calldata, + To: address, + Data: calldata, } txn := extTxn.ToTransaction() @@ -170,6 +170,90 @@ func (api *localShardApi) GetTokens1( ctx context.Context, address types.Address, blockReference rawapitypes.BlockReference, +) (map[types.TokenId]types.Value, error) { + abi, err := contracts.GetAbi(contracts.NameTokenManager) + if err != nil { + return nil, fmt.Errorf("cannot get ABI: %w", err) + } + + calldata, err := abi.Pack("getTokens", address) + if err != nil { + return nil, fmt.Errorf("cannot pack calldata: %w", err) + } + + tokenManagerAddr := types.ShardAndHexToAddress(address.ShardId(), types.TokenManagerPureAddress) + + ret, err := api.CallGetter(ctx, tokenManagerAddr, calldata) + if err != nil { + return nil, fmt.Errorf("failed to call getter: %w", err) + } + + var tokens []token + err = abi.UnpackIntoInterface(&tokens, "getTokens", ret) + if err != nil { + return nil, fmt.Errorf("failed to unpack response: %w", err) + } + + res := make(map[types.TokenId]types.Value) + for t := range tokens { + res[types.TokenId(tokens[t].Token)] = types.NewValueFromBigMust(tokens[t].Balance) + } + return res, nil +} + +func (api *LocalShardApi) CallGetter( + ctx context.Context, + address types.Address, + calldata []byte, +) ([]byte, error) { + tx, err := api.db.CreateRoTx(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + + block, _, err := db.ReadLastBlock(tx, address.ShardId()) + if err != nil { + return nil, fmt.Errorf("failed to read last block: %w", err) + } + + cfgAccessor, err := config.NewConfigReader(tx, &block.MainShardHash) + if err != nil { + return nil, fmt.Errorf("failed to create config accessor: %w", err) + } + + es, err := execution.NewExecutionState(tx, address.ShardId(), execution.StateParams{ + Block: block, + ConfigAccessor: cfgAccessor, + Mode: execution.ModeReadOnly, + }) + if err != nil { + return nil, err + } + + extTxn := &types.ExternalTransaction{ + FeeCredit: types.GasToValue(types.DefaultMaxGasInBlock.Uint64()), + MaxFeePerGas: types.MaxFeePerGasDefault, + To: address, + Data: calldata, + } + + txn := extTxn.ToTransaction() + + payer := execution.NewDummyPayer() + + es.AddInTransaction(txn) + res := es.HandleTransaction(ctx, txn, payer) + if res.Failed() { + return nil, fmt.Errorf("transaction failed: %w", res.GetError()) + } + return res.ReturnData, nil +} + +func (api *LocalShardApi) GetTokens1( + ctx context.Context, + address types.Address, + blockReference rawapitypes.BlockReference, ) (map[types.TokenId]types.Value, error) { shardId := address.ShardId() if shardId != api.shardId() { diff --git a/nil/tests/multitoken/multitoken_test.go b/nil/tests/multitoken/multitoken_test.go index 0c110837c..8461436f8 100644 --- a/nil/tests/multitoken/multitoken_test.go +++ b/nil/tests/multitoken/multitoken_test.go @@ -241,7 +241,6 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint }) }) - s.Run("TestDeployWithToken", func() { tokens := []types.TokenBalance{{Token: *token1.id, Balance: types.NewValueFromUint64(10)}} contractCode, _ := s.LoadContract(common.GetAbsolutePath("../contracts/increment.sol"), "Incrementer") @@ -346,6 +345,9 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint tokenTest1 := CreateTokenId(&s.testAddress1_0) tokenTest2 := CreateTokenId(&s.testAddress1_1) + tokenToSend := types.NewValueFromUint64(5000) + tokenInitial := types.NewValueFromUint64(1_000_000) + defaultFee := types.NewFeePackFromGas(100_000) s.Run("Create tokens for test addresses", func() { s.createTokenForTestContract(tokenTest1, types.NewValueFromUint64(1_000_000), "testToken1") @@ -353,12 +355,13 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint }) s.Run("Call testCallWithTokensSync of testAddress1_0", func() { + data, err := s.abiTest.Pack("testCallWithTokensSync", s.testAddress1_1, - []types.TokenBalance{{Token: *tokenTest1.id, Balance: types.NewValueFromUint64(5000)}}) + []types.TokenBalance{{Token: *tokenTest1.id, Balance: tokenToSend}}) s.Require().NoError(err) hash, err := s.Client.SendExternalTransaction( - s.Context, data, s.testAddress1_0, nil, types.NewFeePackFromGas(100_000)) + s.Context, data, s.testAddress1_0, nil, defaultFee) s.Require().NoError(err) receipt := s.WaitForReceipt(hash) s.Require().True(receipt.Success) @@ -366,11 +369,13 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token is debited from testAddress1_0", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_0, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(1_000_000-5000), tokens[*tokenTest1.id]) + s.Equal(tokenInitial.Sub(tokenToSend), tokens[*tokenTest1.id]) // Check balance via `Nil.tokenBalance` Solidity method + newBalance := tokenInitial.ToBig() + newBalance.Sub(newBalance, tokenToSend.ToBig()) data, err := s.abiTest.Pack( - "checkTokenBalance", types.EmptyAddress, tokenTest1.id, big.NewInt(1_000_000-5000)) + "checkTokenBalance", s.testAddress1_0, tokenTest1.id, newBalance) s.Require().NoError(err) receipt := s.SendExternalTransactionNoCheck(data, s.testAddress1_0) s.Require().True(receipt.Success) @@ -379,7 +384,7 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token is credited to testAddress1_1", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_1, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(5000), tokens[*tokenTest1.id]) + s.Equal(tokenToSend, tokens[*tokenTest1.id]) }) }) @@ -388,7 +393,7 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Try to call with non-existent token", func() { data, err := s.abiTest.Pack("testCallWithTokensSync", s.testAddress1_1, []types.TokenBalance{ - {Token: *tokenTest1.id, Balance: types.NewValueFromUint64(5000)}, + {Token: *tokenTest1.id, Balance: tokenToSend}, {Token: invalidId, Balance: types.NewValueFromUint64(1)}, }) s.Require().NoError(err) @@ -402,23 +407,23 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token of testAddress1_0", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_0, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(1_000_000-5000), tokens[*tokenTest1.id]) + s.Equal(tokenInitial.Sub(tokenToSend), tokens[*tokenTest1.id]) }) s.Run("Check token of testAddress1_1", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_1, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(5000), tokens[*tokenTest1.id]) + s.Equal(tokenToSend, tokens[*tokenTest1.id]) }) }) s.Run("Call testCallWithTokensAsync of testAddress1_0", func() { data, err := s.abiTest.Pack("testCallWithTokensAsync", s.testAddress1_1, - []types.TokenBalance{{Token: *tokenTest1.id, Balance: types.NewValueFromUint64(5000)}}) + []types.TokenBalance{{Token: *tokenTest1.id, Balance: tokenToSend}}) s.Require().NoError(err) hash, err := s.Client.SendExternalTransaction( - s.Context, data, s.testAddress1_0, nil, types.NewFeePackFromGas(100_000)) + s.Context, data, s.testAddress1_0, nil, defaultFee) s.Require().NoError(err) receipt := s.WaitForReceipt(hash) s.Require().True(receipt.Success) @@ -428,26 +433,26 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token is debited from testAddress1_0", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_0, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(1_000_000-5000-5000), tokens[*tokenTest1.id]) + s.Equal(tokenInitial.Sub(tokenToSend).Sub(tokenToSend), tokens[*tokenTest1.id]) }) s.Run("Check token is credited to testAddress1_1", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_1, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(5000+5000), tokens[*tokenTest1.id]) + s.Equal(tokenToSend.Add(tokenToSend), tokens[*tokenTest1.id]) }) }) s.Run("Try to call with non-existent token", func() { data, err := s.abiTest.Pack("testCallWithTokensAsync", s.testAddress1_1, []types.TokenBalance{ - {Token: *tokenTest1.id, Balance: types.NewValueFromUint64(5000)}, + {Token: *tokenTest1.id, Balance: tokenToSend}, {Token: invalidId, Balance: types.NewValueFromUint64(1)}, }) s.Require().NoError(err) hash, err := s.Client.SendExternalTransaction( - s.Context, data, s.testAddress1_0, nil, types.NewFeePackFromGas(100_000)) + s.Context, data, s.testAddress1_0, nil, defaultFee) s.Require().NoError(err) receipt := s.WaitForReceipt(hash) s.Require().False(receipt.Success) @@ -456,13 +461,13 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token of testAddress1_0", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_0, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(1_000_000-5000-5000), tokens[*tokenTest1.id]) + s.Equal(tokenInitial.Sub(tokenToSend).Sub(tokenToSend), tokens[*tokenTest1.id]) }) s.Run("Check token of testAddress1_1", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_1, "latest") s.Require().NoError(err) - s.Equal(types.NewValueFromUint64(5000+5000), tokens[*tokenTest1.id]) + s.Equal(tokenToSend.Add(tokenToSend), tokens[*tokenTest1.id]) }) }) @@ -470,11 +475,11 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint amountTest2 := s.getTokenBalance(&s.testAddress1_1, tokenTest1) s.Run("Call testSendTokensSync", func() { - data, err := s.abiTest.Pack("testSendTokensSync", s.testAddress1_1, big.NewInt(5000), false) + data, err := s.abiTest.Pack("testSendTokensSync", s.testAddress1_1, tokenToSend.ToBig(), false) s.Require().NoError(err) hash, err := s.Client.SendExternalTransaction( - s.Context, data, s.testAddress1_0, nil, types.NewFeePackFromGas(100_000)) + s.Context, data, s.testAddress1_0, nil, defaultFee) s.Require().NoError(err) receipt := s.WaitForReceipt(hash) s.Require().True(receipt.Success) @@ -495,11 +500,11 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint }) s.Run("Call testSendTokensSync with fail flag", func() { - data, err := s.abiTest.Pack("testSendTokensSync", s.testAddress1_1, big.NewInt(5000), true) + data, err := s.abiTest.Pack("testSendTokensSync", s.testAddress1_1, tokenToSend.ToBig(), true) s.Require().NoError(err) hash, err := s.Client.SendExternalTransaction( - s.Context, data, s.testAddress1_0, nil, types.NewFeePackFromGas(100_000)) + s.Context, data, s.testAddress1_0, nil, defaultFee) s.Require().NoError(err) receipt := s.WaitForReceipt(hash) s.Require().False(receipt.Success) @@ -507,24 +512,24 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token of testAddress1_0", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_0, "latest") s.Require().NoError(err) - s.Equal(amountTest1.Sub64(5000), tokens[*tokenTest1.id]) + s.Equal(amountTest1.Sub(tokenToSend), tokens[*tokenTest1.id]) }) s.Run("Check token of testAddress1_1", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_1, "latest") s.Require().NoError(err) - s.Equal(amountTest2.Add64(5000), tokens[*tokenTest1.id]) + s.Equal(amountTest2.Add(tokenToSend), tokens[*tokenTest1.id]) }) }) /////////////////////////////////////////////////////////////////////////// // Call `testSendTokensSync` for address in different shard - should fail s.Run("Fail call testSendTokensSync for address in different shard", func() { - data, err := s.abiTest.Pack("testSendTokensSync", s.smartAccountAddress3, big.NewInt(5000), false) + data, err := s.abiTest.Pack("testSendTokensSync", s.smartAccountAddress3, tokenToSend.ToBig(), false) s.Require().NoError(err) hash, err := s.Client.SendExternalTransaction( - s.Context, data, s.testAddress1_0, nil, types.NewFeePackFromGas(100_000)) + s.Context, data, s.testAddress1_0, nil, defaultFee) s.Require().NoError(err) receipt := s.WaitForReceipt(hash) s.Require().False(receipt.Success) @@ -532,7 +537,7 @@ func (s *SuiteMultiTokenRpc) TestMultiToken() { //nolint s.Run("Check token of testAddress1_0", func() { tokens, err := s.Client.GetTokens(s.Context, s.testAddress1_0, "latest") s.Require().NoError(err) - s.Require().Equal(amountTest1.Sub64(5000), tokens[*tokenTest1.id]) + s.Require().Equal(amountTest1.Add(tokenToSend), tokens[*tokenTest1.id]) }) }) } diff --git a/niljs/src/contract-factory/contracts/ContractFactoryTest.sol b/niljs/src/contract-factory/contracts/ContractFactoryTest.sol index e593e81e4..42dbdc9fc 100644 --- a/niljs/src/contract-factory/contracts/ContractFactoryTest.sol +++ b/niljs/src/contract-factory/contracts/ContractFactoryTest.sol @@ -32,7 +32,7 @@ contract ContractFactoryTest { } function receiveToken() public payable { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it last_token_amount = tokens[0].amount; } } diff --git a/smart-contracts/contracts/Nil.sol b/smart-contracts/contracts/Nil.sol index 628616c30..dcda65026 100644 --- a/smart-contracts/contracts/Nil.sol +++ b/smart-contracts/contracts/Nil.sol @@ -200,9 +200,7 @@ library Nil { Token[] memory tokens, bytes memory callData ) internal returns(bool, bytes memory) { - if (tokens.length > 0) { - __Precompile__(SEND_TOKEN_SYNC).precompileSendTokens(dst, tokens); - } + TokenManager(Nil.getTokenManagerAddress()).transfer(dst, tokens); (bool success, bytes memory returnData) = dst.call{gas: gas, value: value}(callData); return (success, returnData); } @@ -286,7 +284,7 @@ library Nil { * @return Balance of the token. */ function tokenBalance(address addr, TokenId id) internal view returns(uint256) { - return __Precompile__(GET_TOKEN_BALANCE).precompileGetTokenBalance(id, addr); + return TokenManager(Nil.getTokenManagerAddress()).getToken(addr, TokenId.unwrap(id)); } /** @@ -294,7 +292,9 @@ library Nil { * @return Array of tokens from the current transaction. */ function txnTokens() internal returns(Token[] memory) { - return __Precompile__(GET_TRANSACTION_TOKENS).precompileGetTransactionTokens(); + Token[] memory tokens; + require(false, "This function is not implemented"); + return tokens; } /** @@ -503,8 +503,6 @@ contract __Precompile__ { function precompileGetTokenBalance(TokenId id, address addr) public view returns(uint256) {} function precompileAsyncCall(bool, uint8, address, address, address, uint, Nil.Token[] memory, bytes memory) public payable returns(bool) {} function precompileSendRequest(address, Nil.Token[] memory, uint, bytes memory, bytes memory) public payable returns(bool) {} - function precompileSendTokens(address, Nil.Token[] memory) public returns(bool) {} - function precompileGetTransactionTokens() public returns(Nil.Token[] memory) {} function precompileGetGasPrice(uint id) public returns(uint256) {} function precompileGetPoseidonHash(bytes memory data) public returns(uint256) {} function precompileConfigParam(bool isSet, string calldata name, bytes calldata data) public returns(bytes memory) {} diff --git a/smart-contracts/contracts/TokenManager.sol b/smart-contracts/contracts/TokenManager.sol index 6738fa2cb..4ff1c3483 100644 --- a/smart-contracts/contracts/TokenManager.sol +++ b/smart-contracts/contracts/TokenManager.sol @@ -46,6 +46,23 @@ contract TokenManager { emit TokenMinted(msg.sender, token, amount); } + function transfer( + address dst, + Nil.Token[] memory tokens + ) public { + require(Nil.getShardId(address(msg.sender)) == Nil.getShardId(address(dst)), "Shard ID mismatch"); + for (uint i = 0; i < tokens.length; i++) { + address token = TokenId.unwrap(tokens[i].id); + + uint256 oldValue = IterableMapping.get(tokensMap[msg.sender], token); + require(oldValue >= tokens[i].amount, "Insufficient token balance"); + IterableMapping.set(tokensMap[msg.sender], token, oldValue - tokens[i].amount); + + uint256 oldValueDst = IterableMapping.get(tokensMap[dst], token); + IterableMapping.set(tokensMap[dst], token, oldValueDst + tokens[i].amount); + } + } + function totalSupply(address token) view external returns (uint256) { return totalSupplyMap[token]; } @@ -60,4 +77,8 @@ contract TokenManager { } return tokens; } + + function getToken(address account, address token) external view returns (uint256) { + return IterableMapping.get(tokensMap[account], token); + } } \ No newline at end of file diff --git a/uniswap/contracts/UniswapV2Router01.sol b/uniswap/contracts/UniswapV2Router01.sol index c50c4322e..32a5f136b 100644 --- a/uniswap/contracts/UniswapV2Router01.sol +++ b/uniswap/contracts/UniswapV2Router01.sol @@ -18,7 +18,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilTokenBase { } function addLiquidity(address pair, address to) public override { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it if (tokens.length != 2) { revert("Send only 2 tokens to add liquidity"); } @@ -33,7 +33,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilTokenBase { uint amountAMin, uint amountBMin ) public override sameShard(pair) returns (uint amountA, uint amountB) { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it if (tokens.length != 2) { revert("Send only 2 tokens to add liquidity"); } @@ -118,7 +118,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilTokenBase { // **** REMOVE LIQUIDITY **** function removeLiquidity(address pair, address to) public override { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); } @@ -131,7 +131,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilTokenBase { uint /*amountAMin*/, uint /*amountBMin*/ ) public override sameShard(pair) returns (uint amountA, uint amountB) { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); } @@ -153,7 +153,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilTokenBase { uint amount0Out, uint amount1Out ) public override { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); } @@ -174,7 +174,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilTokenBase { uint amountOutMin, address to ) external override sameShard(pair) returns (uint amount) { - Nil.Token[] memory tokens = Nil.txnTokens(); + Nil.Token[] memory tokens = Nil.txnTokens(); // TODO: [PoC] Tokens remove it if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); }