forked from icon-project/xcall-multi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dfde046
commit 4f77e11
Showing
2 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
|