bridge_ui: one step closer to solana transfers
Change-Id: Ief6a8b73458cbfbc7b8d5655ddc6c430a65b2b8f
This commit is contained in:
parent
b1a237db99
commit
3aecf65f4d
|
@ -12,6 +12,7 @@
|
||||||
"@material-ui/core": "^4.12.2",
|
"@material-ui/core": "^4.12.2",
|
||||||
"@metamask/detect-provider": "^1.2.0",
|
"@metamask/detect-provider": "^1.2.0",
|
||||||
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
||||||
|
"@solana/spl-token": "^0.1.6",
|
||||||
"@solana/wallet-base": "^0.0.1",
|
"@solana/wallet-base": "^0.0.1",
|
||||||
"@solana/web3.js": "^1.22.0",
|
"@solana/web3.js": "^1.22.0",
|
||||||
"@typechain/ethers-v5": "^7.0.1",
|
"@typechain/ethers-v5": "^7.0.1",
|
||||||
|
@ -5367,6 +5368,53 @@
|
||||||
"ieee754": "^1.2.1"
|
"ieee754": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@solana/spl-token": {
|
||||||
|
"version": "0.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.1.6.tgz",
|
||||||
|
"integrity": "sha512-fYj+a3w1bqWN6Ibf85XF3h2JkuxevI3Spvqi+mjsNqVUEo2AgxxTZmujNLn/jIzQDNdWkBfF/wYzH5ikcGHmfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.10.5",
|
||||||
|
"@solana/web3.js": "^1.12.0",
|
||||||
|
"bn.js": "^5.1.0",
|
||||||
|
"buffer": "6.0.3",
|
||||||
|
"buffer-layout": "^1.2.0",
|
||||||
|
"dotenv": "10.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@solana/spl-token/node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@solana/spl-token/node_modules/dotenv": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@solana/wallet-base": {
|
"node_modules/@solana/wallet-base": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@solana/wallet-base/-/wallet-base-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@solana/wallet-base/-/wallet-base-0.0.1.tgz",
|
||||||
|
@ -42465,6 +42513,35 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@solana/spl-token": {
|
||||||
|
"version": "0.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.1.6.tgz",
|
||||||
|
"integrity": "sha512-fYj+a3w1bqWN6Ibf85XF3h2JkuxevI3Spvqi+mjsNqVUEo2AgxxTZmujNLn/jIzQDNdWkBfF/wYzH5ikcGHmfw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.10.5",
|
||||||
|
"@solana/web3.js": "^1.12.0",
|
||||||
|
"bn.js": "^5.1.0",
|
||||||
|
"buffer": "6.0.3",
|
||||||
|
"buffer-layout": "^1.2.0",
|
||||||
|
"dotenv": "10.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@solana/wallet-base": {
|
"@solana/wallet-base": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@solana/wallet-base/-/wallet-base-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@solana/wallet-base/-/wallet-base-0.0.1.tgz",
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"@material-ui/core": "^4.12.2",
|
"@material-ui/core": "^4.12.2",
|
||||||
"@metamask/detect-provider": "^1.2.0",
|
"@metamask/detect-provider": "^1.2.0",
|
||||||
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
||||||
|
"@solana/spl-token": "^0.1.6",
|
||||||
"@solana/wallet-base": "^0.0.1",
|
"@solana/wallet-base": "^0.0.1",
|
||||||
"@solana/web3.js": "^1.22.0",
|
"@solana/web3.js": "^1.22.0",
|
||||||
"@typechain/ethers-v5": "^7.0.1",
|
"@typechain/ethers-v5": "^7.0.1",
|
||||||
|
|
|
@ -23,7 +23,7 @@ function KeyAndBalance({
|
||||||
);
|
);
|
||||||
const { wallet: solWallet } = useSolanaWallet();
|
const { wallet: solWallet } = useSolanaWallet();
|
||||||
const solPK = solWallet?.publicKey;
|
const solPK = solWallet?.publicKey;
|
||||||
const solBalance = useSolanaBalance(
|
const { uiAmountString: solBalance } = useSolanaBalance(
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
solPK,
|
solPK,
|
||||||
chainId === CHAIN_ID_SOLANA
|
chainId === CHAIN_ID_SOLANA
|
||||||
|
|
|
@ -100,6 +100,11 @@ function Transfer() {
|
||||||
const provider = useEthereumProvider();
|
const provider = useEthereumProvider();
|
||||||
const { wallet } = useSolanaWallet();
|
const { wallet } = useSolanaWallet();
|
||||||
const solPK = wallet?.publicKey;
|
const solPK = wallet?.publicKey;
|
||||||
|
const {
|
||||||
|
tokenAccount: solTokenPK,
|
||||||
|
decimals: solDecimals,
|
||||||
|
uiAmount: solBalance,
|
||||||
|
} = useSolanaBalance(assetAddress, solPK, fromChain === CHAIN_ID_SOLANA);
|
||||||
// TODO: dynamically get "to" wallet
|
// TODO: dynamically get "to" wallet
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
// TODO: more generic way of calling these
|
// TODO: more generic way of calling these
|
||||||
|
@ -121,27 +126,35 @@ function Transfer() {
|
||||||
transferFrom[fromChain] === transferFromSolana
|
transferFrom[fromChain] === transferFromSolana
|
||||||
) {
|
) {
|
||||||
transferFromSolana(
|
transferFromSolana(
|
||||||
|
wallet,
|
||||||
solPK?.toString(),
|
solPK?.toString(),
|
||||||
|
solTokenPK?.toString(),
|
||||||
assetAddress,
|
assetAddress,
|
||||||
amount,
|
amount,
|
||||||
|
solDecimals,
|
||||||
provider,
|
provider,
|
||||||
toChain
|
toChain
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [fromChain, provider, solPK, assetAddress, amount, toChain]);
|
}, [
|
||||||
|
fromChain,
|
||||||
|
provider,
|
||||||
|
wallet,
|
||||||
|
solPK,
|
||||||
|
solTokenPK,
|
||||||
|
assetAddress,
|
||||||
|
amount,
|
||||||
|
solDecimals,
|
||||||
|
toChain,
|
||||||
|
]);
|
||||||
// update this as we develop, just setting expectations with the button state
|
// update this as we develop, just setting expectations with the button state
|
||||||
const ethBalance = useEthereumBalance(
|
const ethBalance = useEthereumBalance(
|
||||||
assetAddress,
|
assetAddress,
|
||||||
provider,
|
provider,
|
||||||
fromChain === CHAIN_ID_ETH
|
fromChain === CHAIN_ID_ETH
|
||||||
);
|
);
|
||||||
const solBalance = useSolanaBalance(
|
const balance = Number(ethBalance) || solBalance;
|
||||||
assetAddress,
|
|
||||||
solPK,
|
|
||||||
fromChain === CHAIN_ID_SOLANA
|
|
||||||
);
|
|
||||||
const balance = Number(ethBalance) || Number(solBalance);
|
|
||||||
const isTransferImplemented = !!transferFrom[fromChain];
|
const isTransferImplemented = !!transferFrom[fromChain];
|
||||||
const isProviderConnected = !!provider;
|
const isProviderConnected = !!provider;
|
||||||
const isRecipientAvailable = !!solPK;
|
const isRecipientAvailable = !!solPK;
|
||||||
|
|
|
@ -2,20 +2,46 @@ import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { SOLANA_HOST } from "../utils/consts";
|
import { SOLANA_HOST } from "../utils/consts";
|
||||||
|
|
||||||
|
export interface Balance {
|
||||||
|
tokenAccount: PublicKey | undefined;
|
||||||
|
amount: string;
|
||||||
|
decimals: number;
|
||||||
|
uiAmount: number;
|
||||||
|
uiAmountString: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBalance(
|
||||||
|
tokenAccount: PublicKey | undefined,
|
||||||
|
amount: string,
|
||||||
|
decimals: number,
|
||||||
|
uiAmount: number,
|
||||||
|
uiAmountString: string
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
tokenAccount,
|
||||||
|
amount,
|
||||||
|
decimals,
|
||||||
|
uiAmount,
|
||||||
|
uiAmountString,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function useSolanaBalance(
|
function useSolanaBalance(
|
||||||
tokenAddress: string | undefined,
|
tokenAddress: string | undefined,
|
||||||
ownerAddress: PublicKey | null | undefined,
|
ownerAddress: PublicKey | null | undefined,
|
||||||
shouldCalculate?: boolean
|
shouldCalculate?: boolean
|
||||||
) {
|
) {
|
||||||
//TODO: should connection happen in a context?
|
//TODO: should connection happen in a context?
|
||||||
const [balance, setBalance] = useState<string>("");
|
const [balance, setBalance] = useState<Balance>(
|
||||||
|
createBalance(undefined, "", 0, 0, "")
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!tokenAddress || !ownerAddress || !shouldCalculate) {
|
if (!tokenAddress || !ownerAddress || !shouldCalculate) {
|
||||||
setBalance("");
|
setBalance(createBalance(undefined, "", 0, 0, ""));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
const connection = new Connection(SOLANA_HOST);
|
const connection = new Connection(SOLANA_HOST, "finalized");
|
||||||
connection
|
connection
|
||||||
.getParsedTokenAccountsByOwner(ownerAddress, {
|
.getParsedTokenAccountsByOwner(ownerAddress, {
|
||||||
mint: new PublicKey(tokenAddress),
|
mint: new PublicKey(tokenAddress),
|
||||||
|
@ -23,18 +49,23 @@ function useSolanaBalance(
|
||||||
.then(({ value }) => {
|
.then(({ value }) => {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
if (value.length) {
|
if (value.length) {
|
||||||
console.log(value[0].account.data.parsed);
|
|
||||||
setBalance(
|
setBalance(
|
||||||
|
createBalance(
|
||||||
|
value[0].pubkey,
|
||||||
|
value[0].account.data.parsed?.info?.tokenAmount?.amount,
|
||||||
|
value[0].account.data.parsed?.info?.tokenAmount?.decimals,
|
||||||
|
value[0].account.data.parsed?.info?.tokenAmount?.uiAmount,
|
||||||
value[0].account.data.parsed?.info?.tokenAmount?.uiAmountString
|
value[0].account.data.parsed?.info?.tokenAmount?.uiAmountString
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setBalance("0");
|
setBalance(createBalance(undefined, "0", 0, 0, "0"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setBalance("");
|
setBalance(createBalance(undefined, "", 0, 0, ""));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
@ -1,34 +1,42 @@
|
||||||
export type ChainId = 1 | 2 | 3 | 4
|
export type ChainId = 1 | 2 | 3 | 4;
|
||||||
export const CHAIN_ID_SOLANA: ChainId = 1
|
export const CHAIN_ID_SOLANA: ChainId = 1;
|
||||||
export const CHAIN_ID_ETH: ChainId = 2
|
export const CHAIN_ID_ETH: ChainId = 2;
|
||||||
export const CHAIN_ID_TERRA: ChainId = 3
|
export const CHAIN_ID_TERRA: ChainId = 3;
|
||||||
export const CHAIN_ID_BSC: ChainId = 4
|
export const CHAIN_ID_BSC: ChainId = 4;
|
||||||
export interface ChainInfo {
|
export interface ChainInfo {
|
||||||
id: ChainId
|
id: ChainId;
|
||||||
name: string
|
name: string;
|
||||||
}
|
}
|
||||||
export const CHAINS = [
|
export const CHAINS = [
|
||||||
{
|
{
|
||||||
id: CHAIN_ID_BSC,
|
id: CHAIN_ID_BSC,
|
||||||
name: 'Binance Smart Chain'
|
name: "Binance Smart Chain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: CHAIN_ID_ETH,
|
id: CHAIN_ID_ETH,
|
||||||
name: 'Ethereum'
|
name: "Ethereum",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: CHAIN_ID_SOLANA,
|
id: CHAIN_ID_SOLANA,
|
||||||
name: 'Solana'
|
name: "Solana",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: CHAIN_ID_TERRA,
|
id: CHAIN_ID_TERRA,
|
||||||
name: 'Terra'
|
name: "Terra",
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
export type ChainsById = {[key in ChainId]: ChainInfo}
|
export type ChainsById = { [key in ChainId]: ChainInfo };
|
||||||
export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain)=>{obj[chain.id]=chain;return obj},{} as ChainsById)
|
export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
|
||||||
export const SOLANA_HOST = 'http://localhost:8899'
|
obj[chain.id] = chain;
|
||||||
export const ETH_TEST_TOKEN_ADDRESS = "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
return obj;
|
||||||
export const ETH_TOKEN_BRIDGE_ADDRESS = "0xe982e462b094850f12af94d21d470e21be9d0e9c"
|
}, {} as ChainsById);
|
||||||
export const SOL_TEST_TOKEN_ADDRESS = "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ"
|
export const SOLANA_HOST = "http://localhost:8899";
|
||||||
export const SOL_TOKEN_BRIDGE_ADDRESS = "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE"
|
export const ETH_TEST_TOKEN_ADDRESS =
|
||||||
|
"0x0290FB167208Af455bB137780163b7B7a9a10C16";
|
||||||
|
export const ETH_TOKEN_BRIDGE_ADDRESS =
|
||||||
|
"0xe982e462b094850f12af94d21d470e21be9d0e9c";
|
||||||
|
export const SOL_TEST_TOKEN_ADDRESS =
|
||||||
|
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ";
|
||||||
|
export const SOL_BRIDGE_ADDRESS = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
||||||
|
export const SOL_TOKEN_BRIDGE_ADDRESS =
|
||||||
|
"B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
|
||||||
|
|
|
@ -1,12 +1,39 @@
|
||||||
|
import Wallet from "@project-serum/sol-wallet-adapter";
|
||||||
|
import {
|
||||||
|
AccountMeta,
|
||||||
|
Connection,
|
||||||
|
PublicKey,
|
||||||
|
SystemProgram,
|
||||||
|
Transaction,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { arrayify, formatUnits, parseUnits } from "ethers/lib/utils";
|
import { arrayify, formatUnits, parseUnits, zeroPad } from "ethers/lib/utils";
|
||||||
import { Bridge__factory, TokenImplementation__factory } from "../ethers-contracts";
|
import {
|
||||||
import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA, ETH_TOKEN_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS } from "./consts";
|
Bridge__factory,
|
||||||
|
TokenImplementation__factory,
|
||||||
|
} from "../ethers-contracts";
|
||||||
|
import {
|
||||||
|
ChainId,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_HOST,
|
||||||
|
SOL_BRIDGE_ADDRESS,
|
||||||
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
} from "./consts";
|
||||||
|
|
||||||
// TODO: this should probably be extended from the context somehow so that the signatures match
|
// TODO: this should probably be extended from the context somehow so that the signatures match
|
||||||
// TODO: allow for / handle cancellation?
|
// TODO: allow for / handle cancellation?
|
||||||
// TODO: overall better input checking and error handling
|
// TODO: overall better input checking and error handling
|
||||||
export function transferFromEth(provider: ethers.providers.Web3Provider | undefined, tokenAddress: string, amount: string, recipientChain: ChainId, recipientAddress: Uint8Array | undefined) {
|
export function transferFromEth(
|
||||||
|
provider: ethers.providers.Web3Provider | undefined,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
recipientChain: ChainId,
|
||||||
|
recipientAddress: Uint8Array | undefined
|
||||||
|
) {
|
||||||
if (!provider || !recipientAddress) return;
|
if (!provider || !recipientAddress) return;
|
||||||
const signer = provider.getSigner();
|
const signer = provider.getSigner();
|
||||||
if (!signer) return;
|
if (!signer) return;
|
||||||
|
@ -16,11 +43,8 @@ export function transferFromEth(provider: ethers.providers.Web3Provider | undefi
|
||||||
const amountParsed = parseUnits(amount, 18);
|
const amountParsed = parseUnits(amount, 18);
|
||||||
signer.getAddress().then((signerAddress) => {
|
signer.getAddress().then((signerAddress) => {
|
||||||
console.log("Signer:", signerAddress);
|
console.log("Signer:", signerAddress);
|
||||||
console.log("Token:", tokenAddress)
|
console.log("Token:", tokenAddress);
|
||||||
const token = TokenImplementation__factory.connect(
|
const token = TokenImplementation__factory.connect(tokenAddress, signer);
|
||||||
tokenAddress,
|
|
||||||
signer
|
|
||||||
);
|
|
||||||
token
|
token
|
||||||
.allowance(signerAddress, ETH_TOKEN_BRIDGE_ADDRESS)
|
.allowance(signerAddress, ETH_TOKEN_BRIDGE_ADDRESS)
|
||||||
.then((allowance) => {
|
.then((allowance) => {
|
||||||
|
@ -59,41 +83,132 @@ export function transferFromEth(provider: ethers.providers.Web3Provider | undefi
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: should we share this with client? ooh, should client use the SDK ;)
|
||||||
|
// begin from clients\solana\main.ts
|
||||||
|
function ixFromRust(data: any): TransactionInstruction {
|
||||||
|
let keys: Array<AccountMeta> = data.accounts.map(accountMetaFromRust);
|
||||||
|
return new TransactionInstruction({
|
||||||
|
programId: new PublicKey(data.program_id),
|
||||||
|
data: Buffer.from(data.data),
|
||||||
|
keys: keys,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function accountMetaFromRust(meta: any): AccountMeta {
|
||||||
|
return {
|
||||||
|
pubkey: new PublicKey(meta.pubkey),
|
||||||
|
isSigner: meta.is_signer,
|
||||||
|
isWritable: meta.is_writable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// end from clients\solana\main.ts
|
||||||
|
|
||||||
// TODO: need to check transfer native vs transfer wrapped
|
// TODO: need to check transfer native vs transfer wrapped
|
||||||
// TODO: switch out targetProvider for generic address (this likely involves getting these in their respective contexts)
|
// TODO: switch out targetProvider for generic address (this likely involves getting these in their respective contexts)
|
||||||
export function transferFromSolana(fromAddress: string | undefined, tokenAddress: string, amount: string, targetProvider: ethers.providers.Web3Provider | undefined, targetChain: ChainId) {
|
export function transferFromSolana(
|
||||||
if (!fromAddress || !targetProvider) return;
|
wallet: Wallet | undefined,
|
||||||
|
payerAddress: string | undefined, //TODO: we may not need this since we have wallet
|
||||||
|
fromAddress: string | undefined,
|
||||||
|
mintAddress: string,
|
||||||
|
amount: string,
|
||||||
|
decimals: number,
|
||||||
|
targetProvider: ethers.providers.Web3Provider | undefined,
|
||||||
|
targetChain: ChainId
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!wallet ||
|
||||||
|
!wallet.publicKey ||
|
||||||
|
!payerAddress ||
|
||||||
|
!fromAddress ||
|
||||||
|
!targetProvider
|
||||||
|
)
|
||||||
|
return;
|
||||||
const targetSigner = targetProvider.getSigner();
|
const targetSigner = targetProvider.getSigner();
|
||||||
if (!targetSigner) return;
|
if (!targetSigner) return;
|
||||||
targetSigner.getAddress().then(targetAddressStr => {
|
(async () => {
|
||||||
const targetAddress = arrayify(targetAddressStr)
|
const targetAddressStr = await targetSigner.getAddress();
|
||||||
|
const targetAddress = zeroPad(arrayify(targetAddressStr), 32);
|
||||||
const nonceConst = Math.random() * 100000;
|
const nonceConst = Math.random() * 100000;
|
||||||
const nonceBuffer = Buffer.alloc(4);
|
const nonceBuffer = Buffer.alloc(4);
|
||||||
nonceBuffer.writeUInt32LE(nonceConst, 0);
|
nonceBuffer.writeUInt32LE(nonceConst, 0);
|
||||||
const nonce = nonceBuffer.readUInt32LE(0)
|
const nonce = nonceBuffer.readUInt32LE(0);
|
||||||
// TODO: check decimals
|
const amountParsed = parseUnits(amount, decimals).toBigInt();
|
||||||
// should we avoid BigInt?
|
const fee = BigInt(0); // for now, this won't do anything, we may add later
|
||||||
const amountParsed = BigInt(amount)
|
console.log("program:", SOL_TOKEN_BRIDGE_ADDRESS);
|
||||||
const fee = BigInt(0) // for now, this won't do anything, we may add later
|
console.log("bridge:", SOL_BRIDGE_ADDRESS);
|
||||||
console.log('bridge:',SOL_TOKEN_BRIDGE_ADDRESS)
|
console.log("payer:", payerAddress);
|
||||||
console.log('from:',fromAddress)
|
console.log("from:", fromAddress);
|
||||||
console.log('token:',tokenAddress)
|
console.log("token:", mintAddress);
|
||||||
console.log('nonce:',nonce)
|
console.log("nonce:", nonce);
|
||||||
console.log('amount:',amountParsed)
|
console.log("amount:", amountParsed);
|
||||||
console.log('fee:',fee)
|
console.log("fee:", fee);
|
||||||
console.log('target:',targetAddressStr,targetAddress)
|
console.log("target:", targetAddressStr, targetAddress);
|
||||||
console.log('chain:',targetChain)
|
console.log("chain:", targetChain);
|
||||||
// TODO: program_id vs bridge_id?
|
const bridge = await import("bridge");
|
||||||
import("token-bridge").then(({transfer_native_ix})=>{
|
const feeAccount = await bridge.fee_collector_address(SOL_BRIDGE_ADDRESS);
|
||||||
const ix = transfer_native_ix(SOL_TOKEN_BRIDGE_ADDRESS,SOL_TOKEN_BRIDGE_ADDRESS,fromAddress,fromAddress,tokenAddress,nonce,amountParsed,fee,targetAddress,targetChain)
|
const bridgeStatePK = new PublicKey(
|
||||||
console.log(ix)
|
bridge.state_address(SOL_BRIDGE_ADDRESS)
|
||||||
})
|
);
|
||||||
})
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
const bridgeStateAccountInfo = await connection.getAccountInfo(
|
||||||
|
bridgeStatePK
|
||||||
|
);
|
||||||
|
if (bridgeStateAccountInfo?.data === undefined) {
|
||||||
|
throw new Error("bridge state not found");
|
||||||
|
}
|
||||||
|
const bridgeState = bridge.parse_state(
|
||||||
|
new Uint8Array(bridgeStateAccountInfo?.data)
|
||||||
|
);
|
||||||
|
const transferIx = SystemProgram.transfer({
|
||||||
|
fromPubkey: new PublicKey(payerAddress),
|
||||||
|
toPubkey: new PublicKey(feeAccount),
|
||||||
|
lamports: bridgeState.config.fee,
|
||||||
|
});
|
||||||
|
// TODO: pass in connection
|
||||||
|
// Add transfer instruction to transaction
|
||||||
|
const { transfer_native_ix, approval_authority_address } = await import(
|
||||||
|
"token-bridge"
|
||||||
|
);
|
||||||
|
const approvalIx = Token.createApproveInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
new PublicKey(fromAddress),
|
||||||
|
new PublicKey(approval_authority_address(SOL_TOKEN_BRIDGE_ADDRESS)),
|
||||||
|
new PublicKey(payerAddress),
|
||||||
|
[],
|
||||||
|
Number(amountParsed)
|
||||||
|
);
|
||||||
|
const ix = ixFromRust(
|
||||||
|
transfer_native_ix(
|
||||||
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
SOL_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
fromAddress,
|
||||||
|
mintAddress,
|
||||||
|
nonce,
|
||||||
|
amountParsed,
|
||||||
|
fee,
|
||||||
|
targetAddress,
|
||||||
|
targetChain
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log(ix);
|
||||||
|
const transaction = new Transaction().add(transferIx, approvalIx, ix);
|
||||||
|
const { blockhash } = await connection.getRecentBlockhash();
|
||||||
|
transaction.recentBlockhash = blockhash;
|
||||||
|
transaction.feePayer = new PublicKey(payerAddress);
|
||||||
|
// Sign transaction, broadcast, and confirm
|
||||||
|
const signed = await wallet.signTransaction(transaction);
|
||||||
|
console.log("SIGNED", signed);
|
||||||
|
const txid = await connection.sendRawTransaction(signed.serialize());
|
||||||
|
console.log("SENT", txid);
|
||||||
|
await connection.confirmTransaction(txid);
|
||||||
|
console.log("CONFIRMED");
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
const transferFrom = {
|
const transferFrom = {
|
||||||
[CHAIN_ID_ETH]: transferFromEth,
|
[CHAIN_ID_ETH]: transferFromEth,
|
||||||
[CHAIN_ID_SOLANA]: transferFromSolana
|
[CHAIN_ID_SOLANA]: transferFromSolana,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default transferFrom
|
export default transferFrom;
|
||||||
|
|
Loading…
Reference in New Issue