Created
January 6, 2023 20:01
-
-
Save L-Kov/950bce141a9d1aa1ed3b1cfce6d30217 to your computer and use it in GitHub Desktop.
Conditional Tokens Framework Calculations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { keccak256 as solidityKeccak256 } from "@ethersproject/solidity"; | |
import BN from "bn.js"; | |
export const getConditionId = (oracle: string, questionId: string, outcomeSlotCount: number): string => | |
solidityKeccak256(["address", "bytes32", "uint256"], [oracle, questionId, outcomeSlotCount]); | |
const altBN128P = new BN("21888242871839275222246405745257275088696311157297823662689037894645226208583"); | |
const altBN128PRed = BN.red(altBN128P); | |
const altBN128B = new BN(3).toRed(altBN128PRed); | |
const zeroPRed = new BN(0).toRed(altBN128PRed); | |
const onePRed = new BN(1).toRed(altBN128PRed); | |
const twoPRed = new BN(2).toRed(altBN128PRed); | |
const fourPRed = new BN(4).toRed(altBN128PRed); | |
const oddToggle = new BN(1).ushln(254); | |
export const getCollectionId = (conditionId: string, indexSet: number): string => { | |
const initHash = solidityKeccak256(["bytes32", "uint256"], [conditionId, indexSet]); | |
const odd = "89abcdef".includes(initHash[2]); | |
const x = new BN(initHash.slice(2), "hex").toRed(altBN128PRed); | |
let y; | |
let yy: any; | |
do { | |
x.redIAdd(onePRed); | |
yy = x.redSqr(); | |
yy.redIMul(x); | |
yy = yy.mod(altBN128P); | |
yy.redIAdd(altBN128B); | |
y = yy.redSqrt(); | |
} while (!y.redSqr().eq(yy)); | |
const ecHash = x.fromRed(); | |
if (odd) ecHash.ixor(oddToggle); | |
return `0x${ecHash.toString(16, 64)}`; | |
}; | |
export const combineCollectionIds = (collectionIds: string[]): string => { | |
if (Array.isArray(collectionIds) && collectionIds.length === 0) { | |
return `0x${"0".repeat(64)}`; | |
} | |
const points = collectionIds.map(id => { | |
let x = new BN(id); | |
if (x.eqn(0)) { | |
// a zero collection ID represents EC group identity | |
// which is the point at infinity | |
// satisfying projective equation | |
// Y^2 = X^3 + 3*Z^6, Z=0 | |
return [onePRed, onePRed, zeroPRed]; | |
} | |
const odd = x.and(oddToggle).eq(oddToggle); | |
if (odd) x.ixor(oddToggle); | |
x = x.toRed(altBN128PRed); | |
let y; | |
let yy; | |
yy = (x as any).redSqr(); | |
yy = yy.redMul(x); // this might be a BN.js bug workaround | |
yy.redIAdd(altBN128B); | |
y = yy.redSqrt(); | |
if (!y.redSqr().eq(yy)) throw new Error(`got invalid collection ID ${id}`); | |
if (odd !== y.isOdd()) y = y.redNeg(); | |
return [x, y]; | |
}); | |
const [X, Y, Z] = points.reduce(([X1, Y1, Z1], [x2, y2]) => { | |
// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl | |
if (Z1 == null) { | |
// eslint-disable-next-line no-param-reassign | |
Z1 = onePRed; | |
} | |
if (Z1.eqn(0)) { | |
return [x2, y2]; | |
} | |
// source 2007 Bernstein--Lange | |
// assume Z2=1 | |
// compute Z1Z1 = Z1^2 | |
const Z1Z1 = Z1.redSqr(); | |
// compute U2 = X2 Z1Z1 | |
const U2 = x2.redMul(Z1Z1); | |
// compute S2 = Y2 Z1 Z1Z1 | |
const S2 = y2.redMul(Z1).redMul(Z1Z1); | |
// compute H = U2-X1 | |
const H = U2.redSub(X1); | |
// compute HH = H^2 | |
const HH = H.redSqr(); | |
// compute I = 4 HH | |
const I = HH.redMul(fourPRed); | |
// compute J = H I | |
const J = H.redMul(I); | |
// compute r = 2 (S2-Y1) | |
const r = twoPRed.redMul(S2.redSub(Y1)); | |
// compute V = X1 I | |
const V = X1.redMul(I); | |
// compute X3 = r^2-J-2 V | |
const X3 = r | |
.redSqr() | |
.redSub(J) | |
.redSub(twoPRed.redMul(V)); | |
// compute Y3 = r (V-X3)-2 Y1 J | |
const Y3 = r.redMul(V.redSub(X3)).redSub(twoPRed.redMul(Y1).redMul(J)); | |
// compute Z3 = (Z1+H)^2-Z1Z1-HH | |
const Z3 = Z1.redAdd(H) | |
.redSqr() | |
.redSub(Z1Z1) | |
.redSub(HH); | |
return [X3, Y3, Z3]; | |
}); | |
let x; | |
let y; | |
if (Z) { | |
if (Z.eqn(0)) { | |
return `0x${"0".repeat(64)}`; | |
} | |
const invZ = Z.redInvm(); | |
const invZZ = invZ.redSqr(); | |
const invZZZ = invZZ.redMul(invZ); | |
x = X.redMul(invZZ); | |
y = Y.redMul(invZZZ); | |
} else { | |
x = X; | |
y = Y; | |
} | |
const ecHash = x.fromRed(); | |
if (y.isOdd()) ecHash.ixor(oddToggle); | |
return `0x${ecHash.toString(16, 64)}`; | |
}; | |
export const getPositionId = (collateralTokenAddress: string, collectionId: string): string => | |
solidityKeccak256(["address", "uint256"], [collateralTokenAddress, collectionId]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment