"working" frontend
This commit is contained in:
parent
d6ef9c932c
commit
7bae7fbb22
|
@ -1,7 +1,9 @@
|
||||||
|
import {PublicKey} from "@solana/web3.js";
|
||||||
|
|
||||||
const BRIDGE_ADDRESS = "0xac3eB48829fFC3C37437ce4459cE63F1F4d4E0b4";
|
const BRIDGE_ADDRESS = "0xac3eB48829fFC3C37437ce4459cE63F1F4d4E0b4";
|
||||||
|
|
||||||
const SOLANA_BRIDGE_PROGRAM = "9Y7DSe9VGMZPmev93o2JhaAD25ZwQ7d8pPswe9LzL7Eb";
|
const SOLANA_BRIDGE_PROGRAM = new PublicKey("C2MtMo3upLePs5Sumkw61aBgcgpCw9Q84KyJMovLbXu6");
|
||||||
const TOKEN_PROGRAM = "TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
const TOKEN_PROGRAM = new PublicKey("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o");
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
import React, {useContext, useEffect, useState} from 'react';
|
import React, {useContext, useEffect, useState} from 'react';
|
||||||
import ClientContext from "../providers/ClientContext";
|
import ClientContext from "../providers/ClientContext";
|
||||||
import * as solanaWeb3 from '@solana/web3.js';
|
import * as solanaWeb3 from '@solana/web3.js';
|
||||||
import {PublicKey} from '@solana/web3.js';
|
import {Account, Connection, PublicKey, Transaction} from '@solana/web3.js';
|
||||||
import {Button, Col, Form, Input, InputNumber, message, Row, Select, Space} from "antd";
|
import {Button, Col, Form, Input, InputNumber, message, Row, Select, Space} from "antd";
|
||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import {Erc20Factory} from "../contracts/Erc20Factory";
|
import {Erc20Factory} from "../contracts/Erc20Factory";
|
||||||
import {Arrayish, BigNumber, BigNumberish} from "ethers/utils";
|
import {Arrayish, BigNumber, BigNumberish} from "ethers/utils";
|
||||||
import {WormholeFactory} from "../contracts/WormholeFactory";
|
import {WormholeFactory} from "../contracts/WormholeFactory";
|
||||||
import {WrappedAssetFactory} from "../contracts/WrappedAssetFactory";
|
import {WrappedAssetFactory} from "../contracts/WrappedAssetFactory";
|
||||||
import {SolanaBridge} from "../utils/bridge";
|
|
||||||
import {BRIDGE_ADDRESS} from "../config";
|
import {BRIDGE_ADDRESS} from "../config";
|
||||||
import SplBalances from "../components/SplBalances";
|
|
||||||
import {SlotContext} from "../providers/SlotContext";
|
import {SlotContext} from "../providers/SlotContext";
|
||||||
|
import {SolanaTokenContext} from "../providers/SolanaTokenContext";
|
||||||
|
import {BridgeContext} from "../providers/BridgeContext";
|
||||||
|
import SplBalances from "../components/SplBalances";
|
||||||
|
import {AssetMeta, SolanaBridge} from "../utils/bridge";
|
||||||
|
import KeyContext from "../providers/KeyContext";
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -50,9 +53,35 @@ async function approveAssets(asset: string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createWrapped(c: Connection, b: SolanaBridge, key: Account, meta: AssetMeta, mint: PublicKey) {
|
||||||
|
try {
|
||||||
|
let tx = new Transaction();
|
||||||
|
let mintAccount = await c.getAccountInfo(mint);
|
||||||
|
if (!mintAccount) {
|
||||||
|
let ix = await b.createWrappedAssetInstruction(key.publicKey, meta);
|
||||||
|
tx.add(ix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
let [ix_account, newSigner] = await b.createWrappedAssetAndAccountInstructions(key.publicKey, mint);
|
||||||
|
let recentHash = await c.getRecentBlockhash();
|
||||||
|
tx.recentBlockhash = recentHash.blockhash
|
||||||
|
tx.add(...ix_account)
|
||||||
|
tx.sign(key, newSigner)
|
||||||
|
message.loading({content: "Waiting for transaction to be confirmed...", key: "tx", duration: 1000})
|
||||||
|
await c.sendTransaction(tx, [key, newSigner])
|
||||||
|
message.success({content: "Creation succeeded!", key: "tx"})
|
||||||
|
} catch (e) {
|
||||||
|
message.error({content: "Creation failed", key: "tx"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Transfer() {
|
function Transfer() {
|
||||||
let c = useContext<solanaWeb3.Connection>(ClientContext);
|
let c = useContext<solanaWeb3.Connection>(ClientContext);
|
||||||
|
let tokenAccounts = useContext(SolanaTokenContext);
|
||||||
let slot = useContext(SlotContext);
|
let slot = useContext(SlotContext);
|
||||||
|
let bridge = useContext(BridgeContext);
|
||||||
|
let k = useContext(KeyContext);
|
||||||
|
|
||||||
let [coinInfo, setCoinInfo] = useState({
|
let [coinInfo, setCoinInfo] = useState({
|
||||||
balance: new BigNumber(0),
|
balance: new BigNumber(0),
|
||||||
|
@ -60,16 +89,43 @@ function Transfer() {
|
||||||
allowance: new BigNumber(0),
|
allowance: new BigNumber(0),
|
||||||
isWrapped: false,
|
isWrapped: false,
|
||||||
chainID: 0,
|
chainID: 0,
|
||||||
wrappedAddress: ""
|
assetAddress: new Buffer("")
|
||||||
});
|
});
|
||||||
let [amount, setAmount] = useState(0);
|
let [amount, setAmount] = useState(0);
|
||||||
let [address, setAddress] = useState("");
|
let [address, setAddress] = useState("");
|
||||||
let [addressValid, setAddressValid] = useState(false)
|
let [addressValid, setAddressValid] = useState(false)
|
||||||
|
let [solanaAccount, setSolanaAccount] = useState(false)
|
||||||
|
let [wrappedMint, setWrappedMint] = useState("")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchBalance(address)
|
fetchBalance(address)
|
||||||
}, [address])
|
}, [address])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!addressValid) {
|
||||||
|
setWrappedMint("")
|
||||||
|
setSolanaAccount(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let getWrappedInfo = async () => {
|
||||||
|
let wrappedMint = await bridge.getWrappedAssetMint({
|
||||||
|
chain: coinInfo.chainID,
|
||||||
|
address: coinInfo.assetAddress
|
||||||
|
});
|
||||||
|
setWrappedMint(wrappedMint.toString())
|
||||||
|
|
||||||
|
for (let account of tokenAccounts.balances) {
|
||||||
|
if (account.mint == wrappedMint.toString()) {
|
||||||
|
setSolanaAccount(true)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSolanaAccount(false)
|
||||||
|
}
|
||||||
|
getWrappedInfo();
|
||||||
|
}, [address, addressValid, tokenAccounts, bridge])
|
||||||
|
|
||||||
async function fetchBalance(token: string) {
|
async function fetchBalance(token: string) {
|
||||||
try {
|
try {
|
||||||
let e = WrappedAssetFactory.connect(token, provider);
|
let e = WrappedAssetFactory.connect(token, provider);
|
||||||
|
@ -83,8 +139,8 @@ function Transfer() {
|
||||||
allowance: allowance.div(new BigNumber(10).pow(decimals)),
|
allowance: allowance.div(new BigNumber(10).pow(decimals)),
|
||||||
decimals: decimals,
|
decimals: decimals,
|
||||||
isWrapped: false,
|
isWrapped: false,
|
||||||
chainID: 0,
|
chainID: 2,
|
||||||
wrappedAddress: ""
|
assetAddress: new Buffer(token.slice(2), "hex")
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = WormholeFactory.connect(BRIDGE_ADDRESS, provider);
|
let b = WormholeFactory.connect(BRIDGE_ADDRESS, provider);
|
||||||
|
@ -92,7 +148,7 @@ function Transfer() {
|
||||||
let isWrapped = await b.isWrappedAsset(token)
|
let isWrapped = await b.isWrappedAsset(token)
|
||||||
if (isWrapped) {
|
if (isWrapped) {
|
||||||
info.chainID = await e.assetChain()
|
info.chainID = await e.assetChain()
|
||||||
info.wrappedAddress = await e.assetAddress()
|
info.assetAddress = new Buffer((await e.assetAddress()).slice(2), "hex")
|
||||||
info.isWrapped = true
|
info.isWrapped = true
|
||||||
}
|
}
|
||||||
setCoinInfo(info)
|
setCoinInfo(info)
|
||||||
|
@ -106,66 +162,63 @@ function Transfer() {
|
||||||
<>
|
<>
|
||||||
<p>Slot: {slot}</p>
|
<p>Slot: {slot}</p>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col span={12}>
|
||||||
<Space>
|
<Form onFinish={(values) => {
|
||||||
<Form onFinish={(values) => {
|
let recipient = new solanaWeb3.PublicKey(values["recipient"]).toBuffer()
|
||||||
let recipient = new solanaWeb3.PublicKey(values["recipient"]).toBuffer()
|
let transferAmount = new BigNumber(values["amount"]).mul(new BigNumber(10).pow(coinInfo.decimals));
|
||||||
let transferAmount = new BigNumber(values["amount"]).mul(new BigNumber(10).pow(coinInfo.decimals));
|
if (coinInfo.allowance.toNumber() >= amount || coinInfo.isWrapped) {
|
||||||
if (coinInfo.allowance.toNumber() >= amount || coinInfo.isWrapped) {
|
lockAssets(values["address"], transferAmount, recipient, values["target_chain"])
|
||||||
lockAssets(values["address"], transferAmount, recipient, values["target_chain"])
|
} else {
|
||||||
} else {
|
approveAssets(values["address"], transferAmount)
|
||||||
approveAssets(values["address"], transferAmount)
|
}
|
||||||
|
}} style={{width: "100%"}}>
|
||||||
|
<Form.Item name="address" validateStatus={addressValid ? "success" : "error"}>
|
||||||
|
<Input addonAfter={`Balance: ${coinInfo.balance}`} name="address"
|
||||||
|
placeholder={"ERC20 address"}
|
||||||
|
onBlur={(v) => {
|
||||||
|
setAddress(v.target.value)
|
||||||
|
}}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="amount" rules={[{
|
||||||
|
required: true, validator: (rule, value, callback) => {
|
||||||
|
let big = new BigNumber(value);
|
||||||
|
callback(big.lte(coinInfo.balance) ? undefined : "Amount exceeds balance")
|
||||||
}
|
}
|
||||||
}}>
|
}]}>
|
||||||
<Form.Item name="address" validateStatus={addressValid ? "success" : "error"}>
|
<InputNumber name={"amount"} placeholder={"Amount"} type={"number"} onChange={value => {
|
||||||
<Input addonAfter={`Balance: ${coinInfo.balance}`} name="address"
|
// @ts-ignore
|
||||||
placeholder={"ERC20 address"}
|
setAmount(value || 0)
|
||||||
onBlur={(v) => {
|
}}/>
|
||||||
setAddress(v.target.value)
|
</Form.Item>
|
||||||
}}/>
|
<Form.Item name="target_chain"
|
||||||
</Form.Item>
|
rules={[{required: true, message: "Please choose a target chain"}]}>
|
||||||
<Form.Item name="amount" rules={[{
|
<Select placeholder="Target Chain">
|
||||||
required: true, validator: (rule, value, callback) => {
|
<Select.Option value={1}>
|
||||||
let big = new BigNumber(value);
|
Solana
|
||||||
callback(big.lte(coinInfo.balance) ? undefined : "Amount exceeds balance")
|
</Select.Option>
|
||||||
}
|
</Select>
|
||||||
}]}>
|
</Form.Item>
|
||||||
<InputNumber name={"amount"} placeholder={"Amount"} type={"number"} onChange={value => {
|
<Form.Item name="recipient" validateStatus={solanaAccount ? "success" : "error"}>
|
||||||
// @ts-ignore
|
<Input name="recipient" placeholder={"Address of the recipient"}/>
|
||||||
setAmount(value || 0)
|
</Form.Item>
|
||||||
}}/>
|
<Form.Item>
|
||||||
</Form.Item>
|
<Button type="primary" htmlType="submit">
|
||||||
<Form.Item name="target_chain"
|
{coinInfo.allowance.toNumber() >= amount || coinInfo.isWrapped ? "Transfer" : "Approve"}
|
||||||
rules={[{required: true, message: "Please choose a target chain"}]}>
|
</Button>
|
||||||
<Select placeholder="Target Chain">
|
</Form.Item>
|
||||||
<Select.Option value={1}>
|
</Form>
|
||||||
Solana
|
|
||||||
</Select.Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="recipient" rules={[{
|
|
||||||
required: true,
|
|
||||||
validator: (rule, value, callback) => {
|
|
||||||
try {
|
|
||||||
new solanaWeb3.PublicKey(value);
|
|
||||||
callback();
|
|
||||||
} catch (e) {
|
|
||||||
callback("Not a valid Solana address");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},]}>
|
|
||||||
<Input name="recipient" placeholder={"Address of the recipient"}/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item>
|
|
||||||
<Button type="primary" htmlType="submit">
|
|
||||||
{coinInfo.allowance.toNumber() >= amount || coinInfo.isWrapped ? "Transfer" : "Approve"}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Space>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Col>
|
||||||
|
<Space><Button disabled={!addressValid} onClick={() => {
|
||||||
|
createWrapped(c, bridge, k, {
|
||||||
|
chain: coinInfo.chainID,
|
||||||
|
address: coinInfo.assetAddress
|
||||||
|
}, new PublicKey(wrappedMint))
|
||||||
|
}}>Create Wrapped account</Button>
|
||||||
|
<p>Mint: {wrappedMint}</p></Space>
|
||||||
|
<SplBalances/>
|
||||||
|
</Col>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import ClientContext from "../providers/ClientContext";
|
||||||
import * as solanaWeb3 from '@solana/web3.js';
|
import * as solanaWeb3 from '@solana/web3.js';
|
||||||
import {PublicKey, Transaction} from '@solana/web3.js';
|
import {PublicKey, Transaction} from '@solana/web3.js';
|
||||||
import * as spl from '@solana/spl-token';
|
import * as spl from '@solana/spl-token';
|
||||||
import {Button, Col, Form, Input, InputNumber, message, Row, Select, Space} from "antd";
|
import {Button, Col, Form, Input, InputNumber, message, Row, Select} from "antd";
|
||||||
import {BigNumber} from "ethers/utils";
|
import {BigNumber} from "ethers/utils";
|
||||||
import SplBalances from "../components/SplBalances";
|
import SplBalances from "../components/SplBalances";
|
||||||
import {SlotContext} from "../providers/SlotContext";
|
import {SlotContext} from "../providers/SlotContext";
|
||||||
|
@ -60,86 +60,84 @@ function TransferSolana() {
|
||||||
<>
|
<>
|
||||||
<p>Slot: {slot}</p>
|
<p>Slot: {slot}</p>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col span={12}>
|
||||||
<Space>
|
<Form onFinish={(values) => {
|
||||||
<Form onFinish={(values) => {
|
let recipient = new Buffer(values["recipient"].slice(2), "hex");
|
||||||
let recipient = new Buffer(values["recipient"].slice(2), "hex");
|
|
||||||
|
|
||||||
let transferAmount = new BN(values["amount"]).mul(new BN(10).pow(new BN(coinInfo.decimals)));
|
let transferAmount = new BN(values["amount"]).mul(new BN(10).pow(new BN(coinInfo.decimals)));
|
||||||
let fromAccount = new PublicKey(values["address"])
|
let fromAccount = new PublicKey(values["address"])
|
||||||
|
|
||||||
let send = async () => {
|
let send = async () => {
|
||||||
message.loading({content: "Transferring tokens...", key: "transfer"}, 1000)
|
message.loading({content: "Transferring tokens...", key: "transfer"}, 1000)
|
||||||
|
|
||||||
let lock_ix = await bridge.createLockAssetInstruction(k.publicKey, fromAccount, new PublicKey(coinInfo.mint), transferAmount, values["target_chain"], recipient,
|
let lock_ix = await bridge.createLockAssetInstruction(k.publicKey, fromAccount, new PublicKey(coinInfo.mint), transferAmount, values["target_chain"], recipient,
|
||||||
{
|
{
|
||||||
chain: coinInfo.chainID,
|
chain: coinInfo.chainID,
|
||||||
address: coinInfo.wrappedAddress
|
address: coinInfo.wrappedAddress
|
||||||
}, 2);
|
}, 2);
|
||||||
let ix = spl.Token.createApproveInstruction(new PublicKey(TOKEN_PROGRAM), fromAccount, await bridge.getConfigKey(), k.publicKey, [], transferAmount.toNumber())
|
let ix = spl.Token.createApproveInstruction(TOKEN_PROGRAM, fromAccount, await bridge.getConfigKey(), k.publicKey, [], transferAmount.toNumber())
|
||||||
|
|
||||||
let recentHash = await c.getRecentBlockhash();
|
let recentHash = await c.getRecentBlockhash();
|
||||||
let tx = new Transaction();
|
let tx = new Transaction();
|
||||||
tx.recentBlockhash = recentHash.blockhash
|
tx.recentBlockhash = recentHash.blockhash
|
||||||
tx.add(ix)
|
tx.add(ix)
|
||||||
tx.add(lock_ix)
|
tx.add(lock_ix)
|
||||||
tx.sign(k)
|
tx.sign(k)
|
||||||
try {
|
try {
|
||||||
await c.sendTransaction(tx, [k])
|
await c.sendTransaction(tx, [k])
|
||||||
message.success({content: "Transfer succeeded", key: "transfer"})
|
message.success({content: "Transfer succeeded", key: "transfer"})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message.error({content: "Transfer failed", key: "transfer"})
|
message.error({content: "Transfer failed", key: "transfer"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send()
|
||||||
|
}}>
|
||||||
|
<Form.Item name="address" validateStatus={addressValid ? "success" : "error"}>
|
||||||
|
<Input
|
||||||
|
addonAfter={`Balance: ${coinInfo.balance.div(new BigNumber(Math.pow(10, coinInfo.decimals)))}`}
|
||||||
|
name="address"
|
||||||
|
placeholder={"Token account Pubkey"}
|
||||||
|
onBlur={(v) => {
|
||||||
|
setAddress(v.target.value)
|
||||||
|
}}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="amount" rules={[{
|
||||||
|
required: true, validator: (rule, value, callback) => {
|
||||||
|
let big = new BigNumber(value).mul(new BigNumber(10).pow(coinInfo.decimals));
|
||||||
|
callback(big.lte(coinInfo.balance) ? undefined : "Amount exceeds balance")
|
||||||
|
}
|
||||||
|
}]}>
|
||||||
|
<InputNumber name={"amount"} placeholder={"Amount"} type={"number"} onChange={value => {
|
||||||
|
// @ts-ignore
|
||||||
|
setAmount(value || 0)
|
||||||
|
}}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="target_chain"
|
||||||
|
rules={[{required: true, message: "Please choose a target chain"}]}>
|
||||||
|
<Select placeholder="Target Chain">
|
||||||
|
<Select.Option value={2}>
|
||||||
|
Ethereum
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="recipient" rules={[{
|
||||||
|
required: true,
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value.length !== 42 || value.indexOf("0x") != 0) {
|
||||||
|
callback("Invalid address")
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
send()
|
},]}>
|
||||||
}}>
|
<Input name="recipient" placeholder={"Address of the recipient"}/>
|
||||||
<Form.Item name="address" validateStatus={addressValid ? "success" : "error"}>
|
</Form.Item>
|
||||||
<Input
|
<Form.Item>
|
||||||
addonAfter={`Balance: ${coinInfo.balance.div(new BigNumber(Math.pow(10, coinInfo.decimals)))}`}
|
<Button type="primary" htmlType="submit">
|
||||||
name="address"
|
Transfer
|
||||||
placeholder={"Token account Pubkey"}
|
</Button>
|
||||||
onBlur={(v) => {
|
</Form.Item>
|
||||||
setAddress(v.target.value)
|
</Form>
|
||||||
}}/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="amount" rules={[{
|
|
||||||
required: true, validator: (rule, value, callback) => {
|
|
||||||
let big = new BigNumber(value).mul(new BigNumber(10).pow(coinInfo.decimals));
|
|
||||||
callback(big.lte(coinInfo.balance) ? undefined : "Amount exceeds balance")
|
|
||||||
}
|
|
||||||
}]}>
|
|
||||||
<InputNumber name={"amount"} placeholder={"Amount"} type={"number"} onChange={value => {
|
|
||||||
// @ts-ignore
|
|
||||||
setAmount(value || 0)
|
|
||||||
}}/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="target_chain"
|
|
||||||
rules={[{required: true, message: "Please choose a target chain"}]}>
|
|
||||||
<Select placeholder="Target Chain">
|
|
||||||
<Select.Option value={2}>
|
|
||||||
Ethereum
|
|
||||||
</Select.Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="recipient" rules={[{
|
|
||||||
required: true,
|
|
||||||
validator: (rule, value, callback) => {
|
|
||||||
if (value.length !== 42 || value.indexOf("0x") != 0) {
|
|
||||||
callback("Invalid address")
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},]}>
|
|
||||||
<Input name="recipient" placeholder={"Address of the recipient"}/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item>
|
|
||||||
<Button type="primary" htmlType="submit">
|
|
||||||
Transfer
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Space>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<SplBalances/>
|
<SplBalances/>
|
||||||
|
|
|
@ -4,12 +4,12 @@ import solanaWeb3, {Connection, PublicKey} from "@solana/web3.js";
|
||||||
import {SolanaBridge} from "../utils/bridge";
|
import {SolanaBridge} from "../utils/bridge";
|
||||||
import {SOLANA_BRIDGE_PROGRAM, TOKEN_PROGRAM} from "../config";
|
import {SOLANA_BRIDGE_PROGRAM, TOKEN_PROGRAM} from "../config";
|
||||||
|
|
||||||
export const BridgeContext = createContext<SolanaBridge>(new SolanaBridge(new Connection(""), new PublicKey(SOLANA_BRIDGE_PROGRAM), new PublicKey(TOKEN_PROGRAM)));
|
export const BridgeContext = createContext<SolanaBridge>(new SolanaBridge(new Connection(""),SOLANA_BRIDGE_PROGRAM, TOKEN_PROGRAM));
|
||||||
|
|
||||||
export const BridgeProvider: FunctionComponent = ({children}) => {
|
export const BridgeProvider: FunctionComponent = ({children}) => {
|
||||||
let c = useContext<solanaWeb3.Connection>(ClientContext);
|
let c = useContext<solanaWeb3.Connection>(ClientContext);
|
||||||
|
|
||||||
let bridge = new SolanaBridge(c, new PublicKey(SOLANA_BRIDGE_PROGRAM), new PublicKey(TOKEN_PROGRAM))
|
let bridge = new SolanaBridge(c, SOLANA_BRIDGE_PROGRAM,TOKEN_PROGRAM)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BridgeContext.Provider value={bridge}>
|
<BridgeContext.Provider value={bridge}>
|
||||||
|
|
|
@ -40,8 +40,7 @@ export const SolanaTokenProvider: FunctionComponent = ({children}) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
let getAccounts = async () => {
|
let getAccounts = async () => {
|
||||||
try {
|
let res: RpcResponseAndContext<Array<{ pubkey: PublicKey; account: AccountInfo<ParsedAccountData> }>> = await c.getParsedTokenAccountsByOwner(k.publicKey, {programId: TOKEN_PROGRAM}, "single")
|
||||||
let res: RpcResponseAndContext<Array<{ pubkey: PublicKey; account: AccountInfo<ParsedAccountData> }>> = await c.getParsedTokenAccountsByOwner(k.publicKey, {programId: new PublicKey(TOKEN_PROGRAM)}, "single")
|
|
||||||
let meta: AssetMeta[] = [];
|
let meta: AssetMeta[] = [];
|
||||||
for (let acc of res.value) {
|
for (let acc of res.value) {
|
||||||
let am = await b?.fetchAssetMeta(new PublicKey(acc.account.data.parsed.info.mint))
|
let am = await b?.fetchAssetMeta(new PublicKey(acc.account.data.parsed.info.mint))
|
||||||
|
@ -61,10 +60,7 @@ export const SolanaTokenProvider: FunctionComponent = ({children}) => {
|
||||||
})
|
})
|
||||||
setBalances(balances)
|
setBalances(balances)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
} catch (e) {
|
|
||||||
setLoading(false)
|
|
||||||
message.error("Failed to load token accounts")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
getAccounts();
|
getAccounts();
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,8 @@ import BN from 'bn.js';
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import * as BufferLayout from 'buffer-layout'
|
import * as BufferLayout from 'buffer-layout'
|
||||||
|
import {Token} from "@solana/spl-token";
|
||||||
|
import {TOKEN_PROGRAM} from "../config";
|
||||||
|
|
||||||
export interface AssetMeta {
|
export interface AssetMeta {
|
||||||
chain: number,
|
chain: number,
|
||||||
|
@ -25,7 +27,6 @@ class SolanaBridge {
|
||||||
|
|
||||||
async createWrappedAssetInstruction(
|
async createWrappedAssetInstruction(
|
||||||
payer: PublicKey,
|
payer: PublicKey,
|
||||||
amount: number | u64,
|
|
||||||
asset: AssetMeta,
|
asset: AssetMeta,
|
||||||
): Promise<TransactionInstruction> {
|
): Promise<TransactionInstruction> {
|
||||||
const dataLayout = BufferLayout.struct([
|
const dataLayout = BufferLayout.struct([
|
||||||
|
@ -36,12 +37,12 @@ class SolanaBridge {
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let configKey = await this.getConfigKey();
|
let configKey = await this.getConfigKey();
|
||||||
let seeds: Array<Buffer> = [Buffer.from("wrapped"), configKey.toBuffer(), Buffer.of(asset.chain),
|
let seeds: Array<Buffer> = [Buffer.from("wrapped"), configKey.toBuffer(), Buffer.from([asset.chain]),
|
||||||
padBuffer(asset.address, 32)];
|
padBuffer(asset.address, 32)];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let wrappedKey = (await solanaWeb3.PublicKey.findProgramAddress(seeds, this.programID))[0];
|
let wrappedKey = (await solanaWeb3.PublicKey.findProgramAddress(seeds, this.programID))[0];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let wrappedMetaKey = (await solanaWeb3.PublicKey.findProgramAddress([Buffer.from("wrapped"), this.configKey.toBuffer(), wrappedKey.toBuffer()], this.programID))[0];
|
let wrappedMetaKey = (await solanaWeb3.PublicKey.findProgramAddress([Buffer.from("meta"), configKey.toBuffer(), wrappedKey.toBuffer()], this.programID))[0];
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
dataLayout.encode(
|
dataLayout.encode(
|
||||||
|
@ -54,6 +55,7 @@ class SolanaBridge {
|
||||||
);
|
);
|
||||||
|
|
||||||
const keys = [
|
const keys = [
|
||||||
|
{pubkey: this.programID, isSigner: false, isWritable: false},
|
||||||
{pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false},
|
{pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false},
|
||||||
{pubkey: this.tokenProgram, isSigner: false, isWritable: false},
|
{pubkey: this.tokenProgram, isSigner: false, isWritable: false},
|
||||||
{pubkey: configKey, isSigner: false, isWritable: false},
|
{pubkey: configKey, isSigner: false, isWritable: false},
|
||||||
|
@ -133,7 +135,6 @@ class SolanaBridge {
|
||||||
keys.push({pubkey: custodyKey, isSigner: false, isWritable: true})
|
keys.push({pubkey: custodyKey, isSigner: false, isWritable: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
|
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
keys,
|
keys,
|
||||||
|
@ -171,10 +172,61 @@ class SolanaBridge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AccountLayout = BufferLayout.struct([publicKey('mint'), publicKey('owner'), uint64('amount'), BufferLayout.u32('option'), publicKey('delegate'), BufferLayout.u8('is_initialized'), BufferLayout.u8('is_native'), BufferLayout.u16('padding'), uint64('delegatedAmount')]);
|
||||||
|
|
||||||
|
async createWrappedAssetAndAccountInstructions(owner: PublicKey, mint: PublicKey): Promise<[TransactionInstruction[], solanaWeb3.Account]> {
|
||||||
|
const newAccount = new solanaWeb3.Account();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const balanceNeeded = await Token.getMinBalanceRentForExemptAccount(this.connection);
|
||||||
|
let transaction = solanaWeb3.SystemProgram.createAccount({
|
||||||
|
fromPubkey: owner,
|
||||||
|
newAccountPubkey: newAccount.publicKey,
|
||||||
|
lamports: balanceNeeded,
|
||||||
|
space: this.AccountLayout.span,
|
||||||
|
programId: TOKEN_PROGRAM,
|
||||||
|
}); // create the new account
|
||||||
|
|
||||||
|
const keys = [{
|
||||||
|
pubkey: newAccount.publicKey,
|
||||||
|
isSigner: false,
|
||||||
|
isWritable: true
|
||||||
|
}, {
|
||||||
|
pubkey: mint,
|
||||||
|
isSigner: false,
|
||||||
|
isWritable: false
|
||||||
|
}, {
|
||||||
|
pubkey: owner,
|
||||||
|
isSigner: false,
|
||||||
|
isWritable: false
|
||||||
|
}];
|
||||||
|
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
|
||||||
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
|
dataLayout.encode({
|
||||||
|
instruction: 1 // InitializeAccount instruction
|
||||||
|
|
||||||
|
}, data);
|
||||||
|
let ix_init = {
|
||||||
|
keys,
|
||||||
|
programId: TOKEN_PROGRAM,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[transaction.instructions[0], ix_init], newAccount]
|
||||||
|
}
|
||||||
|
|
||||||
async getConfigKey(): Promise<PublicKey> {
|
async getConfigKey(): Promise<PublicKey> {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return (await solanaWeb3.PublicKey.findProgramAddress([Buffer.from("bridge")], this.programID))[0]
|
return (await solanaWeb3.PublicKey.findProgramAddress([Buffer.from("bridge")], this.programID))[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getWrappedAssetMint(asset: AssetMeta): Promise<PublicKey> {
|
||||||
|
let configKey = await this.getConfigKey();
|
||||||
|
let seeds: Array<Buffer> = [Buffer.from("wrapped"), configKey.toBuffer(), Buffer.of(asset.chain),
|
||||||
|
padBuffer(asset.address, 32)];
|
||||||
|
// @ts-ignore
|
||||||
|
return (await solanaWeb3.PublicKey.findProgramAddress(seeds, this.programID))[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from https://github.com/solana-labs/solana-program-library
|
// Taken from https://github.com/solana-labs/solana-program-library
|
||||||
|
@ -215,7 +267,6 @@ export class u64 extends BN {
|
||||||
|
|
||||||
function padBuffer(b: Buffer, len: number): Buffer {
|
function padBuffer(b: Buffer, len: number): Buffer {
|
||||||
const zeroPad = Buffer.alloc(len);
|
const zeroPad = Buffer.alloc(len);
|
||||||
console.log(len - b.length)
|
|
||||||
b.copy(zeroPad, len - b.length);
|
b.copy(zeroPad, len - b.length);
|
||||||
return zeroPad;
|
return zeroPad;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue