basic ETH interaction; WIP Solana interaction
This commit is contained in:
parent
70a1f24220
commit
47464d7600
|
@ -1613,10 +1613,66 @@
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
|
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
|
||||||
},
|
},
|
||||||
|
"@solana/spl-token": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-OXW/zHzMQqVGcSNrNt8sRaHlKT5vjdcUcmUHi8d4ssG8ChbZVA2lkJK10XDXlcnMIiSTindpEjiFmooYc9K3uQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.10.5",
|
||||||
|
"@solana/web3.js": "^0.64.0",
|
||||||
|
"bn.js": "^5.0.0",
|
||||||
|
"buffer-layout": "^1.2.0",
|
||||||
|
"dotenv": "8.2.0",
|
||||||
|
"mkdirp-promise": "^5.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
"@solana/web3.js": {
|
"@solana/web3.js": {
|
||||||
"version": "0.66.1",
|
"version": "0.64.3",
|
||||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.66.1.tgz",
|
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.64.3.tgz",
|
||||||
"integrity": "sha512-AorappmEktL8k0wgJ8nlxbdM3YG+LeeSBBUZUtk+JA2uiRh5pFexsvvViTuTHuzYQbdp66JJyCLc2YcMz8LwEw==",
|
"integrity": "sha512-a20CbWzsA/cRQiPeuwwv21YfcFyHxywpQzAuGmRU7jVv905YzeNgkJ0CxZfN1IZdlgU1SWcFCQVv+DXXCkgWmQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"bn.js": "^5.0.0",
|
||||||
|
"bs58": "^4.0.1",
|
||||||
|
"buffer": "^5.4.3",
|
||||||
|
"buffer-layout": "^1.2.0",
|
||||||
|
"crypto-hash": "^1.2.2",
|
||||||
|
"esdoc-inject-style-plugin": "^1.0.0",
|
||||||
|
"jayson": "^3.0.1",
|
||||||
|
"mz": "^2.7.0",
|
||||||
|
"node-fetch": "^2.2.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"rpc-websockets": "^5.0.8",
|
||||||
|
"superstruct": "^0.8.3",
|
||||||
|
"tweetnacl": "^1.0.0",
|
||||||
|
"ws": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.0.2",
|
||||||
|
"ieee754": "^1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tweetnacl": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "7.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||||
|
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@solana/web3.js": {
|
||||||
|
"version": "0.66.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.66.3.tgz",
|
||||||
|
"integrity": "sha512-HYM1z9E6qVZKEHoLAn5tvL4SioruNB/mvNQhW2v7Va1WbhFArMIEh24SPC23LuEFZVe3PKwlT47zzUhdyua8tQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"bn.js": "^5.0.0",
|
"bn.js": "^5.0.0",
|
||||||
|
@ -3722,13 +3778,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"buffer": {
|
"buffer": {
|
||||||
"version": "4.9.2",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||||
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"base64-js": "^1.0.2",
|
"base64-js": "^1.0.2",
|
||||||
"ieee754": "^1.1.4",
|
"ieee754": "^1.1.4"
|
||||||
"isarray": "^1.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"buffer-from": {
|
"buffer-from": {
|
||||||
|
@ -9939,6 +9994,16 @@
|
||||||
"vm-browserify": "^1.0.1"
|
"vm-browserify": "^1.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"buffer": {
|
||||||
|
"version": "4.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
||||||
|
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.0.2",
|
||||||
|
"ieee754": "^1.1.4",
|
||||||
|
"isarray": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"typescript": "~3.7.2",
|
"typescript": "~3.7.2",
|
||||||
"web3": "^1.2.9",
|
"web3": "^1.2.9",
|
||||||
"@solana/web3.js": "^0.66.1"
|
"@solana/web3.js": "^0.66.3",
|
||||||
|
"@solana/spl-token": "^0.0.5",
|
||||||
|
"buffer-layout": "^1.2.0",
|
||||||
|
"buffer": "^5.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"npm": "^6.14.6",
|
"npm": "^6.14.6",
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
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 {Button, Input, InputNumber, Space} from "antd";
|
import {PublicKey} from '@solana/web3.js';
|
||||||
|
import {Button, Form, Input, InputNumber, message, Select, Space} from "antd";
|
||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import {Erc20Factory} from "../contracts/Erc20Factory";
|
import {Erc20Factory} from "../contracts/Erc20Factory";
|
||||||
import {BigNumber} from "ethers/utils";
|
import {Arrayish, BigNumber, BigNumberish} from "ethers/utils";
|
||||||
|
import {WormholeFactory} from "../contracts/WormholeFactory";
|
||||||
|
import {WrappedAssetFactory} from "../contracts/WrappedAssetFactory";
|
||||||
|
import {SolanaBridge} from "../utils/bridge";
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -13,43 +17,159 @@ window.ethereum.enable();
|
||||||
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||||
const signer = provider.getSigner();
|
const signer = provider.getSigner();
|
||||||
|
|
||||||
|
async function lockAssets(asset: string,
|
||||||
|
amount: BigNumberish,
|
||||||
|
recipient: Arrayish,
|
||||||
|
target_chain: BigNumberish) {
|
||||||
|
let wh = WormholeFactory.connect("0xac3eB48829fFC3C37437ce4459cE63F1F4d4E0b4", signer);
|
||||||
|
try {
|
||||||
|
message.loading({content: "Signing transaction...", key: "eth_tx", duration: 1000},)
|
||||||
|
let res = await wh.lockAssets(asset, amount, recipient, target_chain)
|
||||||
|
message.loading({content: "Waiting for transaction to be mined...", key: "eth_tx", duration: 1000})
|
||||||
|
await res.wait(1);
|
||||||
|
message.success({content: "Transfer on ETH succeeded!", key: "eth_tx"})
|
||||||
|
} catch (e) {
|
||||||
|
message.error({content: "Transfer failed", key: "eth_tx"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function approveAssets(asset: string,
|
||||||
|
amount: BigNumberish) {
|
||||||
|
let e = Erc20Factory.connect(asset, signer);
|
||||||
|
try {
|
||||||
|
message.loading({content: "Signing transaction...", key: "eth_tx", duration: 1000})
|
||||||
|
let res = await e.approve("0xac3eB48829fFC3C37437ce4459cE63F1F4d4E0b4", amount)
|
||||||
|
message.loading({content: "Waiting for transaction to be mined...", key: "eth_tx", duration: 1000})
|
||||||
|
await res.wait(1);
|
||||||
|
message.success({content: "Approval on ETH succeeded!", key: "eth_tx"})
|
||||||
|
} catch (e) {
|
||||||
|
message.error({content: "Approval failed", key: "eth_tx"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Transfer() {
|
function Transfer() {
|
||||||
let c = useContext<solanaWeb3.Connection>(ClientContext);
|
let c = useContext<solanaWeb3.Connection>(ClientContext);
|
||||||
|
|
||||||
let [token, setToken] = useState("");
|
|
||||||
let [balance, setBalance] = useState("0");
|
|
||||||
let [slot, setSlot] = useState(0);
|
let [slot, setSlot] = useState(0);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
c.onSlotChange(value => {
|
c.onSlotChange(value => {
|
||||||
setSlot(value.slot);
|
setSlot(value.slot);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let [coinInfo, setCoinInfo] = useState({
|
||||||
|
balance: new BigNumber(0),
|
||||||
|
decimals: 0,
|
||||||
|
allowance: new BigNumber(0),
|
||||||
|
isWrapped: false,
|
||||||
|
chainID: 0,
|
||||||
|
wrappedAddress: ""
|
||||||
|
});
|
||||||
|
let [amount, setAmount] = useState(0);
|
||||||
|
let [address, setAddress] = useState("");
|
||||||
|
let [addressValid, setAddressValid] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchBalance(){
|
fetchBalance(address)
|
||||||
let e = Erc20Factory.connect(token, provider);
|
}, [address])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function fetchBalance(token: string) {
|
||||||
|
let p = new SolanaBridge(new PublicKey("FHbUryAag7ZfkFKbaCZaqWYsRgEtu7EWFrniy3VQ9Z3w"), new PublicKey("FHbUryAag7ZfkFKbaCZaqWYsRgEtu7EWFrniy3VQ9Z3w"), new PublicKey("FHbUryAag7ZfkFKbaCZaqWYsRgEtu7EWFrniy3VQ9Z3w"))
|
||||||
|
console.log(p.programID.toBuffer())
|
||||||
|
console.log(await p.createWrappedAsset(new PublicKey("FHbUryAag7ZfkFKbaCZaqWYsRgEtu7EWFrniy3VQ9Z3w"), 2000, {
|
||||||
|
chain: 200,
|
||||||
|
|
||||||
|
address: Buffer.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||||
|
}))
|
||||||
try {
|
try {
|
||||||
|
let e = WrappedAssetFactory.connect(token, provider);
|
||||||
let addr = await signer.getAddress();
|
let addr = await signer.getAddress();
|
||||||
let balance = await e.balanceOf(addr);
|
let balance = await e.balanceOf(addr);
|
||||||
let decimals = await e.decimals();
|
let decimals = await e.decimals();
|
||||||
setBalance(balance.div(new BigNumber(10).pow(decimals)).toString());
|
let allowance = await e.allowance(addr, "0xac3eB48829fFC3C37437ce4459cE63F1F4d4E0b4");
|
||||||
}catch (e) {
|
|
||||||
|
|
||||||
|
let info = {
|
||||||
|
balance: balance.div(new BigNumber(10).pow(decimals)),
|
||||||
|
allowance: allowance.div(new BigNumber(10).pow(decimals)),
|
||||||
|
decimals: decimals,
|
||||||
|
isWrapped: false,
|
||||||
|
chainID: 0,
|
||||||
|
wrappedAddress: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = WormholeFactory.connect("0xac3eB48829fFC3C37437ce4459cE63F1F4d4E0b4", provider);
|
||||||
|
|
||||||
|
let isWrapped = await b.isWrappedAsset(token)
|
||||||
|
if (isWrapped) {
|
||||||
|
info.chainID = await e.assetChain()
|
||||||
|
info.wrappedAddress = await e.assetAddress()
|
||||||
|
info.isWrapped = true
|
||||||
|
}
|
||||||
|
setCoinInfo(info)
|
||||||
|
setAddressValid(true)
|
||||||
|
} catch (e) {
|
||||||
|
setAddressValid(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchBalance();
|
|
||||||
}, [token])
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>Slot: {slot}</p>
|
<p>Slot: {slot}</p>
|
||||||
<Space>
|
<Space>
|
||||||
<Input.Group>
|
<Form onFinish={(values) => {
|
||||||
<Input addonAfter={`Balance: ${balance}`} name={"abc"} placeholder={"ERC20 address"}
|
let recipient = new solanaWeb3.PublicKey(values["recipient"]).toBuffer()
|
||||||
onChange={(e) => {
|
let transferAmount = new BigNumber(values["amount"]).mul(new BigNumber(10).pow(coinInfo.decimals));
|
||||||
setToken(e.target.value);
|
if (coinInfo.allowance.toNumber() >= amount || coinInfo.isWrapped) {
|
||||||
|
lockAssets(values["address"], transferAmount, recipient, values["target_chain"])
|
||||||
|
} else {
|
||||||
|
approveAssets(values["address"], transferAmount)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<Form.Item name="address" validateStatus={addressValid ? "success" : "error"}>
|
||||||
|
<Input addonAfter={`Balance: ${coinInfo.balance}`} name="address" placeholder={"ERC20 address"}
|
||||||
|
onBlur={(v) => {
|
||||||
|
setAddress(v.target.value)
|
||||||
}}/>
|
}}/>
|
||||||
<InputNumber name={"amount"} placeholder={"Amount"} type={"number"}/>
|
</Form.Item>
|
||||||
</Input.Group>
|
<Form.Item name="amount" rules={[{
|
||||||
<Button type="primary">Transfer</Button>
|
required: true, validator: (rule, value, callback) => {
|
||||||
|
let big = new BigNumber(value);
|
||||||
|
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={1}>
|
||||||
|
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>
|
</Space>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
import * as solanaWeb3 from "@solana/web3.js";
|
||||||
|
import {PublicKey, TransactionInstruction} from "@solana/web3.js";
|
||||||
|
import BN from 'bn.js';
|
||||||
|
import assert from "assert";
|
||||||
|
// @ts-ignore
|
||||||
|
import * as BufferLayout from 'buffer-layout';
|
||||||
|
|
||||||
|
export interface AssetMeta {
|
||||||
|
chain: number,
|
||||||
|
address: Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
class SolanaBridge {
|
||||||
|
programID: PublicKey;
|
||||||
|
configKey: PublicKey;
|
||||||
|
tokenProgram: PublicKey;
|
||||||
|
|
||||||
|
constructor(programID: PublicKey, configKey: PublicKey, tokenProgram: PublicKey) {
|
||||||
|
this.programID = programID;
|
||||||
|
this.configKey = configKey;
|
||||||
|
this.tokenProgram = tokenProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createWrappedAsset(
|
||||||
|
payer: PublicKey,
|
||||||
|
amount: number | u64,
|
||||||
|
asset: AssetMeta,
|
||||||
|
): Promise<TransactionInstruction> {
|
||||||
|
const dataLayout = BufferLayout.struct([
|
||||||
|
BufferLayout.u8('instruction'),
|
||||||
|
BufferLayout.blob(32, 'address'),
|
||||||
|
BufferLayout.u8('chain'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let seeds: Array<Buffer> = [Buffer.from("wrapped"), this.configKey.toBuffer(), Buffer.of(asset.chain),
|
||||||
|
asset.address];
|
||||||
|
// @ts-ignore
|
||||||
|
let wrappedKey = (await solanaWeb3.PublicKey.findProgramAddress(seeds, this.programID))[0];
|
||||||
|
// @ts-ignore
|
||||||
|
let wrappedMetaKey = (await solanaWeb3.PublicKey.findProgramAddress([Buffer.from("wrapped"), this.configKey.toBuffer(),wrappedKey.toBuffer()], this.programID))[0];
|
||||||
|
|
||||||
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
|
dataLayout.encode(
|
||||||
|
{
|
||||||
|
instruction: 1, // Swap instruction
|
||||||
|
address: asset.address,
|
||||||
|
chain: asset.chain,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false},
|
||||||
|
{pubkey: this.tokenProgram, isSigner: false, isWritable: false},
|
||||||
|
{pubkey: this.configKey, isSigner: false, isWritable: false},
|
||||||
|
{pubkey: payer, isSigner: true, isWritable: true},
|
||||||
|
{pubkey: wrappedKey, isSigner: false, isWritable: true},
|
||||||
|
{pubkey: wrappedMetaKey, isSigner: false, isWritable: true},
|
||||||
|
];
|
||||||
|
return new TransactionInstruction({
|
||||||
|
keys,
|
||||||
|
programId: this.programID,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from https://github.com/solana-labs/solana-program-library
|
||||||
|
// Licensed under Apache 2.0
|
||||||
|
|
||||||
|
export class u64 extends BN {
|
||||||
|
/**
|
||||||
|
* Convert to Buffer representation
|
||||||
|
*/
|
||||||
|
toBuffer(): Buffer {
|
||||||
|
const a = super.toArray().reverse();
|
||||||
|
const b = Buffer.from(a);
|
||||||
|
if (b.length === 8) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
assert(b.length < 8, 'u64 too large');
|
||||||
|
|
||||||
|
const zeroPad = Buffer.alloc(8);
|
||||||
|
b.copy(zeroPad);
|
||||||
|
return zeroPad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a u64 from Buffer representation
|
||||||
|
*/
|
||||||
|
static fromBuffer(buffer: Buffer): u64 {
|
||||||
|
assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`);
|
||||||
|
return new BN(
|
||||||
|
// @ts-ignore
|
||||||
|
[...buffer]
|
||||||
|
.reverse()
|
||||||
|
.map(i => `00${i.toString(16)}`.slice(-2))
|
||||||
|
.join(''),
|
||||||
|
16,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout for a public key
|
||||||
|
*/
|
||||||
|
export const publicKey = (property: string = 'publicKey'): Object => {
|
||||||
|
return BufferLayout.blob(32, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout for a 64bit unsigned value
|
||||||
|
*/
|
||||||
|
export const uint64 = (property: string = 'uint64'): Object => {
|
||||||
|
return BufferLayout.blob(8, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout for a 256-bit unsigned value
|
||||||
|
*/
|
||||||
|
export const uint256 = (property: string = 'uint256'): Object => {
|
||||||
|
return BufferLayout.blob(32, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout for a Rust String type
|
||||||
|
*/
|
||||||
|
export const rustString = (property: string = 'string') => {
|
||||||
|
const rsl = BufferLayout.struct(
|
||||||
|
[
|
||||||
|
BufferLayout.u32('length'),
|
||||||
|
BufferLayout.u32('lengthPadding'),
|
||||||
|
BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'),
|
||||||
|
],
|
||||||
|
property,
|
||||||
|
);
|
||||||
|
const _decode = rsl.decode.bind(rsl);
|
||||||
|
const _encode = rsl.encode.bind(rsl);
|
||||||
|
|
||||||
|
rsl.decode = (buffer: Buffer, offset: number) => {
|
||||||
|
const data = _decode(buffer, offset);
|
||||||
|
return data.chars.toString('utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
rsl.encode = (str: string, buffer: Buffer, offset: number) => {
|
||||||
|
const data = {
|
||||||
|
chars: Buffer.from(str, 'utf8'),
|
||||||
|
};
|
||||||
|
return _encode(data, buffer, offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
return rsl;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {SolanaBridge}
|
Loading…
Reference in New Issue