Add lib.ts

This commit is contained in:
Karl Kempe 2022-05-25 14:25:42 +00:00
parent 18121d6509
commit 73ae68181c
1 changed files with 168 additions and 0 deletions

View File

@ -0,0 +1,168 @@
import { BigNumber, BigNumberish } from "ethers";
import { soliditySha3 } from "web3-utils";
const elliptic = require("elliptic");
// sale struct info
const NUM_BYTES_ACCEPTED_TOKEN = 50;
const NUM_BYTES_ALLOCATION = 65;
export function signAndEncodeVaa(
timestamp: number,
nonce: number,
emitterChainId: number,
emitterAddress: string,
sequence: number,
data: Buffer
): Buffer {
if (Buffer.from(emitterAddress, "hex").length != 32) {
throw Error("emitterAddress != 32 bytes");
}
// wormhole initialized with only one guardian in devnet
const signers = ["cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"];
const sigStart = 6;
const numSigners = signers.length;
const sigLength = 66;
const bodyStart = sigStart + sigLength * numSigners;
const bodyHeaderLength = 51;
const vm = Buffer.alloc(bodyStart + bodyHeaderLength + data.length);
// header
const guardianSetIndex = 0;
vm.writeUInt8(1, 0);
vm.writeUInt32BE(guardianSetIndex, 1);
vm.writeUInt8(numSigners, 5);
// encode body with arbitrary consistency level
const consistencyLevel = 1;
vm.writeUInt32BE(timestamp, bodyStart);
vm.writeUInt32BE(nonce, bodyStart + 4);
vm.writeUInt16BE(emitterChainId, bodyStart + 8);
vm.write(emitterAddress, bodyStart + 10, "hex");
vm.writeBigUInt64BE(BigInt(sequence), bodyStart + 42);
vm.writeUInt8(consistencyLevel, bodyStart + 50);
vm.write(data.toString("hex"), bodyStart + bodyHeaderLength, "hex");
// signatures
const body = vm.subarray(bodyStart).toString("hex");
const hash = soliditySha3(soliditySha3("0x" + body)!)!.substring(2);
for (let i = 0; i < numSigners; ++i) {
const ec = new elliptic.ec("secp256k1");
const key = ec.keyFromPrivate(signers[i]);
const signature = key.sign(hash, { canonical: true });
const start = sigStart + i * sigLength;
vm.writeUInt8(i, start);
vm.write(signature.r.toString(16).padStart(64, "0"), start + 1, "hex");
vm.write(signature.s.toString(16).padStart(64, "0"), start + 33, "hex");
vm.writeUInt8(signature.recoveryParam, start + 65);
//console.log(" beta signature", vm.subarray(start, start + 66).toString("hex"));
}
return vm;
}
export interface AcceptedToken {
address: string; // 32 bytes
chain: number; // uint16
conversionRate: string; // uint128
}
export function encodeAcceptedTokens(acceptedTokens: AcceptedToken[]): Buffer {
const n = acceptedTokens.length;
const encoded = Buffer.alloc(NUM_BYTES_ACCEPTED_TOKEN * n);
for (let i = 0; i < n; ++i) {
const token = acceptedTokens[i];
const start = i * NUM_BYTES_ACCEPTED_TOKEN;
encoded.write(token.address, start, "hex");
encoded.writeUint16BE(token.chain, start + 32);
encoded.write(toBigNumberHex(token.conversionRate, 16), start + 34, "hex");
}
return encoded;
}
export function encodeSaleInit(
saleId: number,
tokenAddress: string, // 32 bytes
tokenChain: number,
tokenAmount: string, // uint256
minRaise: string, // uint256
maxRaise: string, // uint256
saleStart: number,
saleEnd: number,
acceptedTokens: AcceptedToken[], // 50 * n_tokens
recipient: string, // 32 bytes
refundRecipient: string // 32 bytes
): Buffer {
const numTokens = acceptedTokens.length;
const encoded = Buffer.alloc(292 + numTokens * NUM_BYTES_ACCEPTED_TOKEN);
encoded.writeUInt8(1, 0); // initSale payload = 1
encoded.write(toBigNumberHex(saleId, 32), 1, "hex");
encoded.write(tokenAddress, 33, "hex");
encoded.writeUint16BE(tokenChain, 65);
encoded.write(toBigNumberHex(tokenAmount, 32), 67, "hex");
encoded.write(toBigNumberHex(minRaise, 32), 99, "hex");
encoded.write(toBigNumberHex(maxRaise, 32), 131, "hex");
encoded.write(toBigNumberHex(saleStart, 32), 163, "hex");
encoded.write(toBigNumberHex(saleEnd, 32), 195, "hex");
encoded.writeUInt8(numTokens, 227);
encoded.write(encodeAcceptedTokens(acceptedTokens).toString("hex"), 228, "hex");
const recipientIndex = 228 + numTokens * NUM_BYTES_ACCEPTED_TOKEN;
encoded.write(recipient, recipientIndex, "hex");
encoded.write(refundRecipient, recipientIndex + 32, "hex");
return encoded;
}
export interface Allocation {
allocation: BigNumber; // uint256
excessContribution: BigNumber; // uint256
}
export function encodeAllocations(allocations: Allocation[]): Buffer {
const n = allocations.length;
const encoded = Buffer.alloc(NUM_BYTES_ALLOCATION * n);
for (let i = 0; i < n; ++i) {
const item = allocations[i];
const start = i * NUM_BYTES_ALLOCATION;
encoded.writeUint8(i, start);
encoded.write(toBigNumberHex(item.allocation, 32), start + 1, "hex");
encoded.write(toBigNumberHex(item.excessContribution, 32), start + 33, "hex");
}
return encoded;
}
export function encodeSaleSealed(
saleId: number,
allocations: Allocation[] // 65 * n_allocations
): Buffer {
const headerLen = 33;
const numAllocations = allocations.length;
const encoded = Buffer.alloc(headerLen + numAllocations * NUM_BYTES_ALLOCATION);
encoded.writeUInt8(3, 0); // saleSealed payload = 3
encoded.write(toBigNumberHex(saleId, 32), 1, "hex");
encoded.write(encodeAllocations(allocations).toString("hex"), headerLen, "hex");
return encoded;
}
export function encodeSaleAborted(saleId: number): Buffer {
const encoded = Buffer.alloc(33);
encoded.writeUInt8(4, 0); // saleSealed payload = 4
encoded.write(toBigNumberHex(saleId, 32), 1, "hex");
return encoded;
}
function toBigNumberHex(value: BigNumberish, numBytes: number): string {
return BigNumber.from(value)
.toHexString()
.substring(2)
.padStart(numBytes * 2, "0");
}