Skip to content

Commit

Permalink
Added contract audit changes, code review changes
Browse files Browse the repository at this point in the history
  • Loading branch information
kev1n-peters committed Sep 8, 2023
1 parent 20110ce commit 4c4e5eb
Show file tree
Hide file tree
Showing 16 changed files with 3,376 additions and 3,715 deletions.
2 changes: 1 addition & 1 deletion sdk/src/config/TESTNET.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const TESTNET: { [chain in TestnetChainName]: ChainConfig } = {
context: Context.SOLANA,
contracts: {
...CONTRACTS.TESTNET.solana,
relayer: '7PJ8fNkkUQo32SzPNoaw57prhGmRVRvRJhu8Gkvx71wo',
relayer: '3bPRWXqtSfUaCw3S4wdgvypQtsSzcmvDeaqSqPDkncrg',
},
finalityThreshold: 32,
nativeTokenDecimals: 9,
Expand Down
107 changes: 36 additions & 71 deletions sdk/src/contexts/solana/relayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { BN, Program } from '@project-serum/anchor';
import { ChainId } from 'types';
import { NATIVE_MINT } from '@solana/spl-token';
import {
RelayerFee,
deriveRelayerFeeAddress,
RegisteredToken,
deriveRegisteredTokenAddress,
RedeemerConfig,
deriveRedeemerConfigAddress,
ForeignContract,
deriveForeignContractAddress,
} from './utils/tokenBridgeRelayer/accounts';

const SOL_DECIMALS = 9;
const TEN = new BN(10);
const SWAP_RATE_PRECISION = new BN(100_000_000);

export interface SwapEvent {
recipient: string;
Expand All @@ -36,12 +37,10 @@ export class SolanaRelayer {

async isAcceptedToken(mint: string): Promise<boolean> {
try {
const { isRegistered } = await this.getRegisteredToken(
new PublicKey(mint),
);
return isRegistered;
} catch (e) {
if (e instanceof Error && e.message?.includes('Account does not exist')) {
await this.getRegisteredToken(new PublicKey(mint));
return true;
} catch (e: any) {
if (e.message?.includes('Account does not exist')) {
// the token is not registered
return false;
}
Expand All @@ -54,15 +53,14 @@ export class SolanaRelayer {
mint: PublicKey,
decimals: number,
): Promise<bigint> {
const [{ fee }, { swapRate }, { relayerFeePrecision, swapRatePrecision }] =
await Promise.all([
this.getRelayerFee(targetChain),
this.getRegisteredToken(mint),
this.getRedeemerConfig(),
]);
const [{ fee }, { swapRate }, { relayerFeePrecision }] = await Promise.all([
this.getForeignContract(targetChain),
this.getRegisteredToken(mint),
this.getRedeemerConfig(),
]);
const relayerFee = TEN.pow(new BN(decimals))
.mul(fee)
.mul(new BN(swapRatePrecision))
.mul(SWAP_RATE_PRECISION)
.div(new BN(relayerFeePrecision).mul(swapRate));

return BigInt(relayerFee.toString());
Expand All @@ -72,32 +70,16 @@ export class SolanaRelayer {
mint: PublicKey,
decimals: number,
): Promise<bigint> {
const [
{ swapRate, maxNativeSwapAmount },
{ swapRate: solSwapRate },
{ swapRatePrecision },
] = await Promise.all([
this.getRegisteredToken(mint),
this.getRegisteredToken(NATIVE_MINT),
this.getRedeemerConfig(),
]);
const swapRatePrecisionBN = new BN(swapRatePrecision);
const nativeSwapRate = this.calculateNativeSwapRate(
swapRatePrecisionBN,
solSwapRate,
swapRate,
);
const maxSwapAmountIn =
decimals > SOL_DECIMALS
? maxNativeSwapAmount
.mul(nativeSwapRate)
.mul(TEN.pow(new BN(decimals - SOL_DECIMALS)))
.div(swapRatePrecisionBN)
: maxNativeSwapAmount
.mul(nativeSwapRate)
.div(
TEN.pow(new BN(SOL_DECIMALS - decimals)).mul(swapRatePrecisionBN),
);
const [{ swapRate, maxNativeSwapAmount }, { swapRate: solSwapRate }] =
await Promise.all([
this.getRegisteredToken(mint),
this.getRegisteredToken(NATIVE_MINT),
]);
const nativeSwapRate = this.calculateNativeSwapRate(solSwapRate, swapRate);
const maxSwapAmountIn = maxNativeSwapAmount
.mul(nativeSwapRate)
.mul(TEN.pow(new BN(decimals - SOL_DECIMALS)))
.div(SWAP_RATE_PRECISION);

return BigInt(maxSwapAmountIn.toString());
}
Expand All @@ -110,27 +92,14 @@ export class SolanaRelayer {
if (toNativeAmount === 0n) {
return 0n;
}
const [{ swapRate }, { swapRate: solSwapRate }, { swapRatePrecision }] =
await Promise.all([
this.getRegisteredToken(mint),
this.getRegisteredToken(NATIVE_MINT),
this.getRedeemerConfig(),
]);
const swapRatePrecisionBN = new BN(swapRatePrecision);
const nativeSwapRate = this.calculateNativeSwapRate(
swapRatePrecisionBN,
solSwapRate,
swapRate,
);
const swapAmountOut =
decimals > SOL_DECIMALS
? swapRatePrecisionBN
.mul(new BN(toNativeAmount.toString()))
.div(nativeSwapRate.mul(TEN.pow(new BN(decimals - SOL_DECIMALS))))
: swapRatePrecisionBN
.mul(new BN(toNativeAmount.toString()))
.mul(TEN.pow(new BN(SOL_DECIMALS - decimals)))
.div(nativeSwapRate);
const [{ swapRate }, { swapRate: solSwapRate }] = await Promise.all([
this.getRegisteredToken(mint),
this.getRegisteredToken(NATIVE_MINT),
]);
const nativeSwapRate = this.calculateNativeSwapRate(solSwapRate, swapRate);
const swapAmountOut = SWAP_RATE_PRECISION.mul(
new BN(toNativeAmount.toString()),
).div(nativeSwapRate.mul(TEN.pow(new BN(decimals - SOL_DECIMALS))));

return BigInt(swapAmountOut.toString());
}
Expand Down Expand Up @@ -158,17 +127,13 @@ export class SolanaRelayer {
return null;
}

private calculateNativeSwapRate(
swapRatePrecision: BN,
solSwapRate: BN,
swapRate: BN,
): BN {
return swapRatePrecision.mul(solSwapRate).div(swapRate);
private calculateNativeSwapRate(solSwapRate: BN, swapRate: BN): BN {
return SWAP_RATE_PRECISION.mul(solSwapRate).div(swapRate);
}

private async getRelayerFee(chain: ChainId): Promise<RelayerFee> {
return await this.program.account.relayerFee.fetch(
deriveRelayerFeeAddress(this.program.programId, chain),
private async getForeignContract(chain: ChainId): Promise<ForeignContract> {
return await this.program.account.foreignContract.fetch(
deriveForeignContractAddress(this.program.programId, chain),
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { ChainId } from 'types';
import { deriveAddress } from '../../utils';
import { PublicKeyInitData } from '@solana/web3.js';
import { PublicKey, PublicKeyInitData } from '@solana/web3.js';
import { BN } from '@project-serum/anchor';

export interface ForeignEmitter {
chain: ChainId;
address: Buffer;
export interface ForeignContract {
chain: number;
address: number[];
fee: BN;
}

export function deriveForeignContractAddress(
programId: PublicKeyInitData,
chainId: ChainId,
) {
): PublicKey {
const chainIdBuf = Buffer.alloc(2);
chainIdBuf.writeUInt16LE(chainId);
return deriveAddress(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export * from './foreignContract';
export * from './redeemerConfig';
export * from './registeredToken';
export * from './relayerFee';
export * from './senderConfig';
export * from './tmpTokenAccount';
export * from './tokenTransferMessage';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export interface RedeemerConfig {
owner: PublicKey;
bump: number;
relayerFeePrecision: number;
swapRatePrecision: number;
feeRecipient: PublicKey;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { deriveAddress } from '../../utils';
export interface RegisteredToken {
swapRate: BN;
maxNativeSwapAmount: BN;
isRegistered: boolean;
}

export function deriveRegisteredTokenAddress(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ export interface SenderConfig {
owner: PublicKey;
bump: number;
tokenBridge: any;
finality: number;
relayerFeePrecision: number;
swapRatePrecision: number;
paused: boolean;
}

export function deriveSenderConfigAddress(programId: PublicKeyInitData): PublicKey {
export function deriveSenderConfigAddress(
programId: PublicKeyInitData,
): PublicKey {
return deriveAddress([Buffer.from('sender')], programId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { deriveAddress } from '../../utils';
import { PublicKey, PublicKeyInitData } from '@solana/web3.js';
import { BN } from '@project-serum/anchor';

export interface SignerSequence {
value: BN;
}

export function deriveSignerSequenceAddress(
programId: PublicKeyInitData,
payerKey: PublicKeyInitData,
): PublicKey {
return deriveAddress(
[Buffer.from('seq'), new PublicKey(payerKey).toBuffer()],
programId,
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { deriveAddress } from '../../utils';
import { PublicKey, PublicKeyInitData } from '@solana/web3.js';
import { BN } from '@project-serum/anchor';

export function deriveTokenTransferMessageAddress(
programId: PublicKeyInitData,
sequence: bigint,
payer: PublicKeyInitData,
sequence: BN,
): PublicKey {
const sequenceBuf = Buffer.alloc(8);
sequenceBuf.writeBigUInt64LE(sequence);
return deriveAddress([Buffer.from('bridged'), sequenceBuf], programId);
sequenceBuf.writeBigUInt64LE(BigInt(sequence.toString()));
return deriveAddress(
[Buffer.from('bridged'), new PublicKey(payer).toBuffer(), sequenceBuf],
programId,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ import {
deriveSenderConfigAddress,
deriveTokenTransferMessageAddress,
deriveRegisteredTokenAddress,
deriveRelayerFeeAddress,
deriveTmpTokenAccountAddress,
} from '../accounts';
import { getProgramSequenceTracker } from '../../wormhole';
import { getAssociatedTokenAddressSync } from '@solana/spl-token';
import { BN } from '@project-serum/anchor';
import { ChainId } from 'types';
import { deriveSignerSequenceAddress } from '../accounts/signerSequence';

export async function createTransferNativeTokensWithRelayInstruction(
connection: Connection,
Expand All @@ -35,13 +34,20 @@ export async function createTransferNativeTokensWithRelayInstruction(
): Promise<TransactionInstruction> {
const {
methods: { transferNativeTokensWithRelay },
account: { signerSequence },
} = createTokenBridgeRelayerProgramInterface(programId, connection);
const { sequence } = await getProgramSequenceTracker(
connection,
tokenBridgeProgramId,
wormholeProgramId,
);
const message = deriveTokenTransferMessageAddress(programId, sequence);
const signerSequenceAddress = deriveSignerSequenceAddress(programId, payer);
const sequence = await signerSequence
.fetch(signerSequenceAddress)
.then(({ value }) => value)
.catch((e) => {
if (e.message?.includes('Account does not exist')) {
// first time transferring
return new BN(0);
}
throw e;
});
const message = deriveTokenTransferMessageAddress(programId, payer, sequence);
const fromTokenAccount = getAssociatedTokenAddressSync(
new PublicKey(mint),
new PublicKey(payer),
Expand All @@ -66,9 +72,9 @@ export async function createTransferNativeTokensWithRelayInstruction(
)
.accounts({
config: deriveSenderConfigAddress(programId),
payerSequence: signerSequenceAddress,
foreignContract: deriveForeignContractAddress(programId, recipientChain),
registeredToken: deriveRegisteredTokenAddress(programId, mint),
relayerFee: deriveRelayerFeeAddress(programId, recipientChain),
tmpTokenAccount,
tokenBridgeProgram: new PublicKey(tokenBridgeProgramId),
...tokenBridgeAccounts,
Expand Down
Loading

0 comments on commit 4c4e5eb

Please sign in to comment.