Skip to content

Commit

Permalink
chore(sdk): solana gencode use bigint (stephenh#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
zfy0701 authored Dec 31, 2022
1 parent f503560 commit e7a7edd
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 55 deletions.
3 changes: 2 additions & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
"docs": "typedoc --options typedoc.json"
},
"dependencies": {
"@dao-xyz/borsh": "^4.0.7",
"@ethersproject/providers": "~5.7.0",
"@project-serum/anchor": "^0.26.0",
"@sentio/cli": "^1.0.0",
"@sentio/base": "^1.0.0",
"@sentio/cli": "^1.0.0",
"@sentio/protos": "^1.0.0",
"@solana/web3.js": "^1.47.3",
"@typechain/ethers-v5": "^10.0.0",
Expand Down
147 changes: 119 additions & 28 deletions packages/sdk/src/builtin/solana/wormhole-processor.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { SolanaBaseProcessor, SolanaContext, SolanaBindOptions } from '@sentio/sdk'
import { Instruction } from '@project-serum/anchor'
import * as borsh from "@coral-xyz/borsh";
import bs58 from 'bs58'

import {
deserialize,
field,
vec, fixedArray, option,
} from "@dao-xyz/borsh";

// https://github.com/certusone/wormhole/blob/8818d4b8f0471095dd48fa6f5da9c315cbfc9b52/solana/modules/token_bridge/program/src/lib.rs#L100
enum TokenBridgeInstruction {
Initialize = 0,
Expand All @@ -20,30 +25,104 @@ enum TokenBridgeInstruction {
TransferNativeWithPayload,
}

class TransferDataValues {
@field({ type: 'u32' })
nonce: number

@field({type: 'u64'})
amount: bigint

@field({type: 'u64'})
fee: bigint

@field({ type: fixedArray('u8', 32) })
target_address: number[];

@field({ type: 'u16' })
target_chain: number

constructor(data?: TransferDataValues) {
if(data){
Object.assign(this,data)
}
}
}

class TransferNativeWithPayloadData {
@field({ type: 'u32' })
nonce: number

@field({type: 'u64'})
amount: bigint

@field({type: 'u64'})
fee: bigint

@field({ type: fixedArray('u8', 32) })
target_address: number[];

@field({ type: 'u16' })
target_chain: number

@field({ type: vec('u8') })
payload: number[];

@field({type: option(fixedArray('u8', 32) )})
cpi_program_id?: number[]

constructor(data?: TransferNativeWithPayloadData) {
if(data){
Object.assign(this,data)
}
}
}

class AttestTokenValues {
@field({ type: 'u32' })
nonce: number

constructor(data?: AttestTokenValues) {
if(data){
Object.assign(this,data)
}
}
}

class InitializeDataValues {
@field({type: fixedArray('u8', 32) })
bridge: number[]

constructor(data?: InitializeDataValues) {
if(data){
Object.assign(this,data)
}
}
}

export class TokenBridgeProcessor extends SolanaBaseProcessor {

private readonly transferDataValues = [
borsh.u32('nonce'),
borsh.u64('amount'),
borsh.u64('fee'),
borsh.array(borsh.u8(undefined), 32, 'target_address'),
borsh.u16('target_chain')
]
// private readonly transferDataValues = [
// borsh.u32('nonce'),
// borsh.u64('amount'),
// borsh.u64('fee'),
// borsh.array(borsh.u8(undefined), 32, 'target_address'),
// borsh.u16('target_chain')
// ]
// https://github.com/certusone/wormhole/blob/8818d4b8f0471095dd48fa6f5da9c315cbfc9b52/solana/modules/token_bridge/program/src/api/transfer_payload.rs#L170
private readonly transferDataWithPayloadValues = [
borsh.u32('nonce'),
borsh.u64('amount'),
borsh.array(borsh.u8(undefined), 32, 'target_address'),
borsh.u16('target_chain'),
borsh.vecU8('payload'),
borsh.option(borsh.publicKey(undefined), 'cpi_program_id')
]
private readonly attestTokenValues = [
borsh.u32('nonce')
]
private readonly initializeDataValues = [
borsh.publicKey('bridge')
]
// private readonly transferDataWithPayloadValues = [
// borsh.u32('nonce'),
// borsh.u64('amount'),
// borsh.array(borsh.u8(undefined), 32, 'target_address'),
// borsh.u16('target_chain'),
// borsh.vecU8('payload'),
// borsh.option(borsh.publicKey(undefined), 'cpi_program_id')
// ]
// private readonly attestTokenValues = [
// borsh.u32('nonce')
// ]
// private readonly initializeDataValues = [
// borsh.publicKey('bridge')
// ]

static bind(options: SolanaBindOptions): TokenBridgeProcessor {
if (options && !options.name) {
Expand All @@ -67,39 +146,51 @@ export class TokenBridgeProcessor extends SolanaBaseProcessor {
name: TokenBridgeInstruction[TokenBridgeInstruction.Initialize]
}
case TokenBridgeInstruction.TransferNative:
data = deserialize(Buffer.from(u8Arr.slice(1)), TransferDataValues)

// struct is defined at: https://github.com/certusone/wormhole/blob/8818d4b8f0471095dd48fa6f5da9c315cbfc9b52/solana/modules/token_bridge/program/src/api/transfer.rs#L295
data = borsh.struct(this.transferDataValues, 'TransferNativeData').decode(Buffer.from(u8Arr.slice(1)))
// data = borsh.struct(this.transferDataValues, 'TransferNativeData').decode(Buffer.from(u8Arr.slice(1)))
return {
data,
name: TokenBridgeInstruction[TokenBridgeInstruction.TransferNative]
}
case TokenBridgeInstruction.TransferWrapped:
data = deserialize(Buffer.from(u8Arr.slice(1)), TransferDataValues)

// stuct is defined at: https://github.com/certusone/wormhole/blob/8818d4b8f0471095dd48fa6f5da9c315cbfc9b52/solana/modules/token_bridge/program/src/api/transfer.rs#L295
data = borsh.struct(this.transferDataValues, 'TransferWrappedData').decode(Buffer.from(u8Arr.slice(1)))
// data = borsh.struct(this.transferDataValues, 'TransferWrappedData').decode(Buffer.from(u8Arr.slice(1)))
return {
data,
name: TokenBridgeInstruction[TokenBridgeInstruction.TransferWrapped]
}
case TokenBridgeInstruction.TransferNativeWithPayload:
data = borsh.struct(this.transferDataWithPayloadValues, 'TransferNativeWithPayloadData').decode(Buffer.from(u8Arr.slice(1)))
data = deserialize(Buffer.from(u8Arr.slice(1)), TransferNativeWithPayloadData)

// data = borsh.struct(this.transferDataWithPayloadValues, 'TransferNativeWithPayloadData').decode(Buffer.from(u8Arr.slice(1)))
return {
data,
name: TokenBridgeInstruction[TokenBridgeInstruction.TransferNativeWithPayload]
}
case TokenBridgeInstruction.TransferWrappedWithPayload:
data = borsh.struct(this.transferDataWithPayloadValues, 'TransferWrappedWithPayloadData').decode(Buffer.from(u8Arr.slice(1)))
data = deserialize(Buffer.from(u8Arr.slice(1)), TransferNativeWithPayloadData)

// data = borsh.struct(this.transferDataWithPayloadValues, 'TransferWrappedWithPayloadData').decode(Buffer.from(u8Arr.slice(1)))
return {
data,
name: TokenBridgeInstruction[TokenBridgeInstruction.TransferWrappedWithPayload]
}
case TokenBridgeInstruction.Initialize:
data = borsh.struct(this.initializeDataValues, 'InitializeData').decode(Buffer.from(u8Arr.slice(1)))
data = deserialize(Buffer.from(u8Arr.slice(1)), InitializeDataValues)

// data = borsh.struct(this.initializeDataValues, 'InitializeData').decode(Buffer.from(u8Arr.slice(1)))
return {
data,
name: TokenBridgeInstruction[TokenBridgeInstruction.Initialize]
}
case TokenBridgeInstruction.AttestToken:
data = borsh.struct(this.attestTokenValues, 'AttestTokenData').decode(Buffer.from(u8Arr.slice(1)))
data = deserialize(Buffer.from(u8Arr.slice(1)), AttestTokenValues)

// data = borsh.struct(this.attestTokenValues, 'AttestTokenData').decode(Buffer.from(u8Arr.slice(1)))
return {
data,
name: TokenBridgeInstruction[TokenBridgeInstruction.AttestToken]
Expand Down
23 changes: 8 additions & 15 deletions packages/sdk/src/core/numberish.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { BigNumber } from 'ethers'
import { BigInteger, MetricValue } from '@sentio/protos'
import { BigDecimal } from '.'
import { BN } from '@project-serum/anchor'
import { BlockTag } from '@ethersproject/providers'

export type Numberish = number | BigNumber | bigint | BigDecimal
export type Numberish = number | BigNumber | bigint | BigDecimal | string

export function toBlockTag(a: number | bigint): BlockTag {
if (typeof a === 'number') {
Expand Down Expand Up @@ -41,9 +40,9 @@ export function toMetricValue(value: Numberish): MetricValue {
})
}
}
if (BN.isBN(value)) {
if (typeof value === 'string') {
return MetricValue.fromPartial({
bigInteger: bnToBigInteger(value),
bigDecimal: value,
})
}
if (typeof value === 'bigint' || Number.isInteger(value)) {
Expand Down Expand Up @@ -74,14 +73,6 @@ function bigDecimalToBigInteger(a: BigDecimal): BigInteger {
return hexToBigInteger(a.toString(16), negative)
}

function bnToBigInteger(a: BN): BigInteger {
const negative = a.isNeg()
if (negative) {
a = a.abs()
}
return hexToBigInteger(a.toString(16), negative)
}

function intToBigInteger(a: bigint | number): BigInteger {
const negative = a < 0
if (negative) {
Expand All @@ -94,12 +85,14 @@ export function toBigInteger(a: Numberish): BigInteger {
if (a instanceof BigDecimal) {
return bigDecimalToBigInteger(a)
}
if (a instanceof BN) {
return bnToBigInteger(a)
}

if (a instanceof BigNumber) {
return intToBigInteger(a.toBigInt())
}
if (typeof a === 'string') {
return intToBigInteger(BigInt(a))
}

return intToBigInteger(a)

// Following code is actually very slow
Expand Down
27 changes: 20 additions & 7 deletions packages/sdk/src/solana-codegen/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function codeGenSolanaIdlProcessor(idlObj: any): string {
const idlName = idlObj.name
const idlNamePascalCase = toPascalCase(idlName)
const instructions: any[] = idlObj.instructions
return `import { BorshInstructionCoder, Instruction, Idl, BN } from '@project-serum/anchor'
return `import { BorshInstructionCoder, Instruction, Idl } from '@project-serum/anchor'
import { SolanaBaseProcessor, SolanaContext, SolanaBindOptions } from "@sentio/sdk"
import { ${idlName}_idl } from "./${idlName}"
import bs58 from 'bs58'
Expand All @@ -59,14 +59,15 @@ export class ${idlNamePascalCase}Processor extends SolanaBaseProcessor {

function codeGenSolanaInstruction(idlName: string, ins: any): string {
const instructionName = ins.name
const argsTypeString = codeGenInstructionArgs(ins.args)
return `
on${
instructionName.charAt(0).toUpperCase() + instructionName.slice(1)
}(handler: (args: ${argsTypeString}, accounts: string[], ctx: SolanaContext) => void): ${idlName}Processor {
on${instructionName.charAt(0).toUpperCase() + instructionName.slice(1)}(handler: (args: ${codeGenInstructionArgsType(
ins.args
)}, accounts: string[], ctx: SolanaContext) => void): ${idlName}Processor {
this.onInstruction('${instructionName}', (ins: Instruction, ctx, accounts: string[]) => {
const origin = ins.data as any
const data = ${codeGenInstructionArgs(ins.args)}
if (ins) {
handler(ins.data as ${argsTypeString}, accounts, ctx)
handler(data, accounts, ctx)
}
})
return this
Expand All @@ -75,6 +76,18 @@ function codeGenSolanaInstruction(idlName: string, ins: any): string {
}

function codeGenInstructionArgs(args: { name: string; type: string }[]): string {
return `{ ${args.map((arg) => codeGenInstructionArg(arg.name, arg.type)).join(', ')} }`
}

function codeGenInstructionArg(name: string, type: string): string {
const mType = mapType(type)
if (mType === 'bigint') {
return `${name}: BigInt(origin.${name}.toString())`
}
return `${name}: origin.${name} as ${mType}`
}

function codeGenInstructionArgsType(args: { name: string; type: string }[]): string {
return `{ ${args.map((arg) => arg.name + ': ' + mapType(arg.type)).join(', ')} }`
}

Expand All @@ -100,7 +113,7 @@ function mapType(tpe: string): string {
case 'i64':
case 'u128':
case 'i128':
return 'BN'
return 'bigint'
default:
return 'any'
}
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/tests/solana.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('Test Solana Example', () => {
expect(res.result?.counters).length(1)
expect(res.result?.gauges).length(0)
expect(res.result?.counters[0].metadata?.blockNumber).equal(0n)
expect(firstCounterValue(res.result, 'totalWeth_supply')).equal(12000000000000)
expect(firstCounterValue(res.result, 'totalWeth_supply')?.toString()).equal('12000000000000')
expect(res.result?.counters[0].runtimeInfo?.from).equals(HandlerType.SOL_INSTRUCTION)
})
})
4 changes: 2 additions & 2 deletions packages/sdk/src/tests/wormhole-token-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ TokenBridgeProcessor.bind({
SPLTokenProcessor.bind({ address: 'wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb', processInnerInstruction: true })
.onMintTo((data, ctx) => {
if (data.mint === '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs') {
ctx.meter.Counter('totalWeth_supply').add(data.amount as number)
ctx.meter.Counter('totalWeth_supply').add(BigInt(data.amount))
}
})
.onBurn((data, ctx) => {
if (data.mint === '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs') {
ctx.meter.Counter('totalWeth_supply').sub(data.amount as number)
ctx.meter.Counter('totalWeth_supply').sub(BigInt(data.amount))
}
})
1 change: 1 addition & 0 deletions packages/sdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true,
"inlineSources": true,
"rootDir": "./src",
"baseUrl": "./src",
Expand Down
9 changes: 8 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,13 @@
enabled "2.0.x"
kuler "^2.0.0"

"@dao-xyz/borsh@^4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@dao-xyz/borsh/-/borsh-4.0.7.tgz#e70f2b646e0a72f01024e537a9eb2b015f2746fd"
integrity sha512-YvpI4jPenXDpBpKsQu6ZIYTKJej5XQdacLYH5mFJUiz08/TOTnM2mywKZE0BNZi13ujtLK5x4pDFk5YoFfEhaQ==
dependencies:
"@protobufjs/utf8" "^1.1.0"

"@discoveryjs/json-ext@^0.5.0":
version "0.5.7"
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
Expand Down Expand Up @@ -746,7 +753,7 @@

"@grpc/grpc-js@^1.6.1":
version "1.7.3"
resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz"
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.7.3.tgz#f2ea79f65e31622d7f86d4b4c9ae38f13ccab99a"
integrity sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==
dependencies:
"@grpc/proto-loader" "^0.7.0"
Expand Down

0 comments on commit e7a7edd

Please sign in to comment.