Skip to content

Commit

Permalink
add xcall bridge snippets
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonAndell committed Nov 6, 2024
1 parent dfde046 commit 4f77e11
Show file tree
Hide file tree
Showing 2 changed files with 388 additions and 0 deletions.
144 changes: 144 additions & 0 deletions sdk/src/test-mainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { SuiIntents } from './sui-intents';
import { EVMIntents } from './evm-intents';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { JsonRpcProvider } from '@ethersproject/providers';
import { Wallet } from '@ethersproject/wallet';
import fs from 'fs';
import { SwapOrder } from "./swap-order";
import { EVMAssetManager } from "./xcall-bridge";
import { assert } from "console";

const getProvider = () => {
const providerUrl = "https://arb1.arbitrum.io/rpc";
return new JsonRpcProvider(providerUrl);
};

const arbNID = "0xa4b1.arbitrum"
const suiNID = "sui"
const SUIToken = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI"
const ethToken = "0x0000000000000000000000000000000000000000"
const intentContract = "0x53E0095C57673fC16fA3FA2414bAD3200844Ec17";

const packageId = "0xbf8044a8f498b43e48ad9ad8a7d23027a45255903e8b4765dda38da2d1b89600"
const storageId = "0x78e96d7acd208baba0c37c1fd5d193088fa8f5ea45d18fa4c32eb3721307529d"

const keystore = fs.readFileSync('/home/andell/ICON/intent-contracts/contracts/evm/.keystores/balanced_testnet_eth', 'utf8');
const password = '9re$u{}V-W';
const evmWallet = Wallet.fromEncryptedJsonSync(keystore, password);
const evmAddress = evmWallet.address
const evm = new EVMIntents(intentContract, getProvider(), evmWallet);
const assetManager = new EVMAssetManager("0x78b7CD9308287DEb724527d8703c889e2d6C3708", getProvider(), evmWallet);

const keypair = Ed25519Keypair.fromSecretKey("suiprivkey1qrdjntsdyuygqjsx3varvvx2hz6xkuk86zu2y255s0zw97uavl82xjul9z6");
const sui = new SuiIntents(packageId, storageId, "mainnet", keypair)
const suiAddress = keypair.getPublicKey().toSuiAddress()

const evmToSUI = async () => {
const evmReceiver = Wallet.createRandom().address;
const suiReceiver = Ed25519Keypair.generate().getPublicKey().toSuiAddress();
const order = new SwapOrder(
BigInt(0),
intentContract,
arbNID,
suiNID,
evmAddress,
suiReceiver,
ethToken,
BigInt(100),
SUIToken,
BigInt(101),
new Uint8Array()
)
const tx = await evm.orderETHNative(order);
await tx.wait()
const createdOrder = await evm.getOrder(tx.hash);

// order is set by contract
order.id = createdOrder.id

assert(createdOrder.equals(order))

const digest = await sui.fillOrder(createdOrder, evmReceiver);
await sui.client.waitForTransaction({digest: digest.digest})
const fill = await sui.getBalance(SUIToken, suiReceiver)
console.log("After fill", fill)
const fee = Number(order.toAmount)/Number(10000)
assert(Number(fill) == Number(order.toAmount) - fee)

const start = Date.now();
while (true) {
const payout = await evm.getBalance(ethToken, evmReceiver);

// Check if the payout matches the order amount
if (payout === order.amount) {

const duration = Math.floor((Date.now() - start) / 1000); // Calculate the elapsed time in seconds
console.log(`payout passed in ${duration} seconds`);
return; // Exit the function if the assertion passes
}

// Wait for a shorter interval before retrying
await new Promise(r => setTimeout(r, 1000)); // Retry every 1 second
}
};


const SUIToEVM = async () => {
const evmReceiver = Wallet.createRandom().address;
const suiReceiver = Ed25519Keypair.generate().getPublicKey().toSuiAddress();
const order = new SwapOrder(
BigInt(0),
storageId,
suiNID,
arbNID,
suiAddress,
evmReceiver,
SUIToken,
BigInt(100),
ethToken,
BigInt(101),
new Uint8Array()
)
const tx = await sui.createOrder(order);
const createdOrder = await sui.getOrder(tx.digest);

order.id = createdOrder.id
assert(createdOrder.equals(order))

const fillTx = await evm.fillOrder(createdOrder, suiReceiver);
await fillTx.wait()

const fill = await evm.getBalance(ethToken, evmReceiver);
const fee = Math.floor((Number(order.toAmount)*10)/10000)
console.log(fill)
assert(Number(fill) == Number(order.toAmount) - fee)
const start = Date.now();
while (true) {
const payout = await sui.getBalance(SUIToken, suiReceiver);

// Check if the payout matches the order amount
if (payout === order.amount) {

const duration = (Date.now() - start) / 1000; // Calculate the elapsed time in seconds
console.log(`payout passed in ${duration} seconds`);
return; // Exit the function if the assertion passes
}

// Wait for a shorter interval before retrying
await new Promise(r => setTimeout(r, 1000)); // Retry every 1 second
}

//verify original funds released

};
// evmToSUI();
// SUIToEVM();
const approve = async () => {
await assetManager.approve("0xaf88d065e77c8cC2239327C5EDb3A432268e5831", 1000);
};
const send = async () => {
await assetManager.bridgeUSDCToSUI("0xaf88d065e77c8cC2239327C5EDb3A432268e5831", 1, "0xc940f195be990bbf4171fb2ede315f4149e8e95f039a0a5be4a84cb5e1b5d873");
};

send()

244 changes: 244 additions & 0 deletions sdk/src/xcall-bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import { JsonRpcProvider } from '@ethersproject/providers';
import { Contract } from '@ethersproject/contracts';
import { Wallet } from '@ethersproject/wallet';
import fs from 'fs';
import { PERMIT2_ADDRESS, PermitTransferFrom, SignatureTransfer, Witness } from '@uniswap/permit2-sdk';
import { _TypedDataEncoder } from '@ethersproject/hash'
import { MaxUint256, Interface} from 'ethers';
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { Transaction } from '@mysten/sui/transactions';


const erc20Abi = [
{
"constant": false,
"inputs": [
{
"name": "spender",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
];

const assetManagerAbi = [
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "string",
"name": "to",
"type": "string"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "deposit",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},

{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "depositNative",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "string",
"name": "to",
"type": "string"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "depositNative",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "string",
"name": "to",
"type": "string"
}
],
"name": "depositNative",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
];


export class EVMAssetManager{
address: string;
provider: JsonRpcProvider;
assetManager: Contract;
wallet!: Wallet;
// this.address = address;
// 0x78b7CD9308287DEb724527d8703c889e2d6C3708
constructor(address: string, provider: JsonRpcProvider, wallet: Wallet) {
this.address = address;
this.provider = provider;
this.wallet = wallet.connect(this.provider);

this.assetManager = new Contract(this.address, assetManagerAbi, this.wallet);
}


async approve(token: string, amount: number) {
const erc20 = new Contract(token, erc20Abi, this.wallet);
await erc20.approve(this.assetManager.address, amount);
}

toDeadline(expiration: number): number {
return Math.floor((Date.now() + expiration) / 1000)
}

public async bridgeUSDCToSUI(usdcAddress:string, amount: number, suiAddress:string) {
const data = {"method": "_swap", "params": {
"path": [],
"receiver": "sui/"+suiAddress
}}
const _data = new TextEncoder().encode(JSON.stringify(data));
this.assetManager.deposit(usdcAddress, amount, "cx21e94c08c03daee80c25d8ee3ea22a20786ec231", _data, {value: 60000000000000})
};
}


export class SuiAssetManager {
client: SuiClient;
keypair: Ed25519Keypair;

constructor(keypair: Ed25519Keypair) {
const rpcUrl = getFullnodeUrl("mainnet");
this.keypair = keypair;
this.client = new SuiClient({ url: rpcUrl });
}

private async getCoin(tx: Transaction, coin: string, amount: bigint) {
const coins = await this.client.getCoins({
owner: this.keypair.getPublicKey().toSuiAddress(),
coinType: coin,
});

let objects: string[] = [];
let totalAmount = BigInt(0);

for (const coin of coins.data) {
totalAmount += BigInt(coin.balance);
objects.push(coin.coinObjectId);

if (totalAmount >= amount) {
break;
}
}

if (objects.length > 1) {
tx.mergeCoins(objects[0], objects.slice(1));
}

if (totalAmount === amount) {
return objects[0];
}

return tx.splitCoins(objects[0], [amount]);
}

async bridgeUSDCToSUI(usdcAddress:string, amount: number, nid:string, evmAddress:string): Promise<any> {
const tx = new Transaction();
let coin: any;

coin = await this.getCoin(tx, usdcAddress, BigInt(amount));
const data = {"method": "_swap", "params": {
"path": [],
"receiver": nid + "/" + evmAddress
}}
const _data = new TextEncoder().encode(JSON.stringify(data));

tx.moveCall({
target: `0xd5aa24a346fd89468d13f00f57162df8a498ec11c197df2bc4257ad74fa977b1::asset_manager::deposit`,
arguments: [
tx.object("0x25c200a947fd16903d9daea8e4c4b96468cf08d002394b7f1933b636e0a0d500"),
tx.object("0xe9ae3e2d32cdf659ad5db4219b1086cc0b375da5c4f8859c872148895a2eace2"),
tx.object("0x1bbf52529d14124738fac0abc1386670b7927b6d68cab7f9bd998a0c0b274042"),
tx.pure.string(nid),
tx.pure.u128(evmAddress),
tx.pure.vector('vector<u8>', [].slice.call(_data)),

],
typeArguments: [usdcAddress],
});

const result = await this.client.signAndExecuteTransaction({ signer: this.keypair, transaction: tx });
return result;
}

async getBalance(token: string , address: string ): Promise<BigInt> {
const coins = await this.client.getCoins({
owner: address,
coinType: token,
});

var sum = BigInt(0);
for (const coin of coins.data) {
sum += BigInt(coin.balance);
}

return sum
}
}

0 comments on commit 4f77e11

Please sign in to comment.