Add lib.ts
This commit is contained in:
parent
18121d6509
commit
73ae68181c
|
@ -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");
|
||||
}
|
Loading…
Reference in New Issue