Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sending tokens from Aurora to Near #10

Closed
Tarnadas opened this issue May 31, 2023 · 9 comments
Closed

sending tokens from Aurora to Near #10

Tarnadas opened this issue May 31, 2023 · 9 comments

Comments

@Tarnadas
Copy link

Hey,

I am trying to send tokens from Aurora to Near via ft_transfer_call, but I suppose I'm doing something wrong, because idk how the Near <-> Aurora bridge actually works. I am probably missing some sort of unlock.
My solidity contract is fairly simple, so here it is:

// SPDX-License-Identifier: CC-BY-1.0
pragma solidity ^0.8.17;

import 'openzeppelin-contracts/access/AccessControl.sol';
import 'openzeppelin-contracts/token/ERC20/IERC20.sol';
import { AuroraSdk, Codec, NEAR, PromiseCreateArgs, PromiseResultStatus, PromiseWithCallback } from 'aurora-sdk/AuroraSdk.sol';

uint64 constant FT_TRANSFER_CALL_NEAR_GAS = 50_000_000_000_000;
uint64 constant FT_ON_TRANSFER_NEAR_GAS = 20_000_000_000_000;

contract OrderlyDex is AccessControl {
  using AuroraSdk for NEAR;
  using AuroraSdk for PromiseCreateArgs;
  using AuroraSdk for PromiseWithCallback;
  using Codec for bytes;

  bytes32 public constant CALLBACK_ROLE = keccak256('CALLBACK_ROLE');

  IERC20 public wNEAR;
  string public dexAccountId;
  NEAR public near;

  constructor(string memory _dexAccountId, IERC20 _wNEAR) {
    dexAccountId = _dexAccountId;
    near = AuroraSdk.initNear(_wNEAR);
    wNEAR = _wNEAR;
    _grantRole(
      CALLBACK_ROLE,
      AuroraSdk.nearRepresentitiveImplicitAddress(address(this))
    );
  }

  function deposit(
    IERC20 token,
    string memory tokenIdOnNear,
    uint128 amount
  ) public {
    token.transferFrom(msg.sender, address(this), amount);
    bytes memory data = abi.encodePacked(
      '{',
      '"receiver_id": "',
      dexAccountId,
      '",',
      '"amount": "',
      amount,
      '",',
      '"msg": "Deposit"',
      '}'
    );
    PromiseCreateArgs memory callFtTransfer = near.call(
      tokenIdOnNear,
      'ft_transfer_call',
      data,
      1,
      FT_TRANSFER_CALL_NEAR_GAS
    );
    PromiseCreateArgs memory callback = near.auroraCall(
      address(this),
      abi.encodePacked(this.depositCallback.selector),
      0,
      FT_ON_TRANSFER_NEAR_GAS
    );
    callFtTransfer.then(callback).transact();
  }

  function depositCallback() public onlyRole(CALLBACK_ROLE) {
    if (AuroraSdk.promiseResult(0).status != PromiseResultStatus.Successful) {
      revert('Call to ft_transfer_call failed');
    }
  }
}

When running an integration test it seems like the tokens aren't moving at all.
Unfortunately I could also not find an example how to send tokens from Aurora to Near. I would also need to be able to receive tokens, like in a swap.
If you need to I can also show my current integration test.

@Tarnadas
Copy link
Author

Tarnadas commented May 31, 2023

Update:

Here's what I came up with:

  function deposit(
    IERC20 token,
    string memory tokenIdOnNear,
    uint128 amount
  ) public {
    token.transferFrom(msg.sender, address(this), amount);

    bytes memory input = abi.encodePacked(uint(amount));
    input[0] = 0x01;
    (bool ok, ) = address(0xE9217BC70B7ED1f598ddD3199e80b093fA71124F).call(
      abi.encodePacked(input, AuroraSdk.currentAccountId())
    );
    require(ok);

    // ...

According to the docs I can call this precompile to bridge erc20 back to Near. Somehow my tx gets reverted, so I'll need to investigate this.
My plan was to bridge to Aurora contract on Near and then it should be able to call the ft_transfer_call. I'm not entirely sure what the target address should be where I bridge it to. Is it the mapping account, so should I use AuroraSdk.nearRepresentative(address(this)) instead of AuroraSdk.currentAccountId()?

@birchmd
Copy link
Member

birchmd commented May 31, 2023

Hi @Tarnadas

Could you explain what you are trying to do on a conceptual level? (What kinds of tokens are involved, what users/contracts control them, etc). Then I might be able to help you with the technical details more efficiently.

One key point to understand is that it matters whether the token is Near-native or not (i.e. it existed as a NEP-141 token first then was bridged to Aurora as an ERC-20).

@Tarnadas
Copy link
Author

Tarnadas commented May 31, 2023

Hey @birchmd,

I have been developing an on chain orderbook DEX for Orderly Network and want to develop a proxy contract to seamlessly use it from Aurora without the need of token bridging.

I wanted to start easy and do a deposit which is done via ft_transfer_call. It also includes more complex things like a swap where the msg data for the transfer is somewhat similar to ref finance. It also has functions to do limit or market orders either via deposited tokens or via transferred tokens.

The tokens can be either native NEP-141 or native ERC-20, but the dapp will be able to find out.

@birchmd
Copy link
Member

birchmd commented May 31, 2023

Thanks @Tarnadas . Let me see if I understand this correctly. We are assuming that there is some Aurora user Alice who does not have any Near account. We want to design a way for Alice to submit an order to the Orderly orderbook on Near. Submitting an order involves depositing some asset into the orderbook contract. For simplicity we must assume that the assets Alice will be depositing are bridged to Aurora so that they all have NEP-141 counterparts. It is possible to make something that works with Aurora-native ERC-20 tokens, but it introduces a lot more complexity, so unless this is a hard requirement for you then I think we ignore these assets for now.

For the technical solution to this, there will be two steps. First using the exit_to_near to move Alice's ERC-20 token to it's NEP-141 counterpart, owned by Alice's XCC account (of the form <ADDRESS>.aurora). Then Alice can make the XCC precompile call to instruct Alice's account to make the ft_transfer_call which deposits the funds. I don't think there is a good way to do this in a single Aurora transaction.

An alternative solution, which might be able to do it all in a single Aurora transaction would be to have an EVM contract make all the necessary calls, but then if all users use the same contract it would not be possible to distinguish who deposited the tokens on the Near side I don't think.

@Tarnadas
Copy link
Author

Tarnadas commented Jun 1, 2023

Yes you understand it correctly. The order creation is designed that it can also be done without a prior deposit via ft_transfer_call and a proper value for its msg field, but for now I will start with the deposit. I would also need native ERC-20 token support, but let's start with NEP-141.

How can I use the exit_to_near precompile from within a Solidity smart contract? In my above code I tried to create a respective input to interact with the precompile, but in the docs is a note saying "For completeness, the details are included anyways even though you can not directly interface with this precompile."

I am more interested in the solution with a single tx. If it's done from within the Solidity smart contract it would still be the XCC account that sends the tokens to the orderbook, so I would be able to use that address to distinguish them I assume?

It would be great to have an example to interact with Ref Finance from Aurora and perform a swap. Similar to the Uniswap example, but the other way around.

@birchmd
Copy link
Member

birchmd commented Jun 1, 2023

How can I use the exit_to_near precompile from within a Solidity smart contract?

You do not use the precompile directly. You call the function that is available on the bridged ERC-20 tokens (that function is actually called withdrawToNear, so that's how I should have called it in my previous comment).

it would still be the XCC account that sends the tokens to the orderbook

There isn't just one XCC account. A different account is created for each address that calls the XCC precompile (of the form <ADDRESS>.aurora). So if you were to use an EVM contract, at address 0xcontract_address, to make the XCC call then the Near account the orderbook contract would see is contract_address.aurora, regardless of which Aurora user interacted with the EVM contract. You may be able to distinguish different users if you add extra information to the msg field in ft_transfer_call, but you won't be able to distinguish users on the account level.

@Tarnadas
Copy link
Author

Tarnadas commented Jun 1, 2023

Oh so my misunderstanding was that I thought for every user who interacts with the contract a different account will be created. The way you described it, it is actually a lot better and also makes a lot more sense, because of the 2N funding requirement. Looks like I need to use the msg field then

@Tarnadas
Copy link
Author

Tarnadas commented Jun 1, 2023

Right now I'm trying to figure out where my tokens went after calling withdrawToNear, but somehow I'm unable to find them. I created an interface for IEvmErc20 in a separate file copying the function declaration and extending ERC20. I then give it to my deposit function as type for the tokens.

My Solidity contract deposit function looks like this:

  function deposit(
    IEvmErc20 token,
    string memory tokenIdOnNear,
    uint128 amount
  ) public {
    token.transferFrom(msg.sender, address(this), amount);

    token.withdrawToNear(
      abi.encodePacked(AuroraSdk.nearRepresentative(address(this))),
      uint(amount)
    );
    // ...

I commented out the rest of the code, so there are no more token transfers involved.

In my integration test I assume that the address the tokens should now be deposited on Near is this address:

let account_key = format!("{}.{}", proxy_contract.address.encode(), engine.inner.id());

but somehow they are not here. proxy_contract is the Solidity contract that has been deployed.

To check for the token balance on Near I simply called ft_balance_of for respective account ID.

Update:

Fixed the address encoding with abi.encodePacked and tried out various address and checked their balances. I cannot seem to find the tokens on the Near side even if I e.g. hardcode something like "a.test.near" as recipient, which should be the Aurora engine address

@birchmd
Copy link
Member

birchmd commented Nov 17, 2023

Closing this issue as it has been resolved with the inclusion of the ft_refund example in #13

@birchmd birchmd closed this as completed Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants