@0xsequence/core
Advanced tools
Comparing version 0.0.0-20230621220927 to 0.0.0-20230622143614
@@ -25,2 +25,3 @@ import { ethers } from 'ethers'; | ||
encode: (data: T | Z | ethers.BytesLike) => string; | ||
trim: (data: string) => Promise<string>; | ||
recover: (data: Z, payload: SignedPayload, provider: ethers.providers.Provider) => Promise<T>; | ||
@@ -27,0 +28,0 @@ supportsNoChainId: boolean; |
@@ -34,2 +34,5 @@ import { ethers } from "ethers"; | ||
export declare function isTopology(topology: any): topology is Topology; | ||
export declare function encodeSignerLeaf(leaf: SignerLeaf): string; | ||
export declare function decodeSignerLeaf(encoded: string): SignerLeaf; | ||
export declare function isEncodedSignerLeaf(encoded: string): boolean; | ||
export declare function hashNode(node: Node | Leaf): string; | ||
@@ -36,0 +39,0 @@ export declare function leftFace(topology: Topology): Topology[]; |
@@ -108,2 +108,8 @@ import { BigNumberish, ethers } from "ethers"; | ||
export declare function signaturesOfDecoded(utopology: UnrecoveredTopology): string[]; | ||
export declare function subdigestsOfDecoded(utopology: UnrecoveredTopology): string[]; | ||
export declare function trimSignature(signature: string | UnrecoveredSignature): Promise<string>; | ||
export declare function trimUnrecoveredTree(tree: UnrecoveredTopology, trimStaticDigest?: boolean): Promise<{ | ||
weight: number; | ||
trimmed: UnrecoveredTopology; | ||
}>; | ||
export declare const SignatureCoder: base.SignatureCoder<WalletConfig, Signature, UnrecoveredChainedSignature | UnrecoveredSignature>; |
{ | ||
"name": "@0xsequence/core", | ||
"version": "0.0.0-20230621220927", | ||
"version": "0.0.0-20230622143614", | ||
"description": "core primitives for interacting with the sequence wallet contracts", | ||
@@ -23,3 +23,3 @@ "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/core", | ||
"dependencies": { | ||
"@0xsequence/abi": "0.0.0-20230621220927" | ||
"@0xsequence/abi": "0.0.0-20230622143614" | ||
}, | ||
@@ -26,0 +26,0 @@ "scripts": { |
@@ -36,2 +36,4 @@ | ||
trim: (data: string) => Promise<string>, | ||
recover: ( | ||
@@ -38,0 +40,0 @@ data: Z, |
@@ -229,2 +229,6 @@ | ||
trim: async (data: string): Promise<string> => { | ||
return data | ||
}, | ||
supportsNoChainId: true, | ||
@@ -231,0 +235,0 @@ |
@@ -44,3 +44,3 @@ | ||
export function isSubdigestLeaf(leaf: any): leaf is SubdigestLeaf { | ||
return (leaf as SubdigestLeaf).subdigest !== undefined | ||
return (leaf as SubdigestLeaf).subdigest !== undefined && (leaf as SignerLeaf).address === undefined | ||
} | ||
@@ -140,8 +140,36 @@ | ||
export function encodeSignerLeaf(leaf: SignerLeaf): string { | ||
return ethers.utils.solidityPack( | ||
['uint96', 'address'], | ||
[leaf.weight, leaf.address] | ||
) | ||
} | ||
export function decodeSignerLeaf(encoded: string): SignerLeaf { | ||
const bytes = ethers.utils.arrayify(encoded); | ||
if (bytes.length !== 32) { | ||
throw new Error('Invalid encoded string length'); | ||
} | ||
const weight = ethers.BigNumber.from(bytes.slice(0, 12)); | ||
const address = ethers.utils.getAddress(ethers.utils.hexlify(bytes.slice(12))); | ||
return { weight, address } | ||
} | ||
export function isEncodedSignerLeaf(encoded: string): boolean { | ||
const bytes = ethers.utils.arrayify(encoded) | ||
if (bytes.length !== 32) { | ||
return false | ||
} | ||
const prefix = bytes.slice(0, 11) | ||
return prefix.every((byte) => byte === 0) | ||
} | ||
export function hashNode(node: Node | Leaf): string { | ||
if (isSignerLeaf(node)) { | ||
return ethers.utils.solidityPack( | ||
['uint96', 'address'], | ||
[node.weight, node.address] | ||
) | ||
return encodeSignerLeaf(node) | ||
} | ||
@@ -148,0 +176,0 @@ |
import { BigNumberish, ethers } from "ethers" | ||
import { isValidSignature, recoverSigner, tryRecoverSigner } from "../commons/signer" | ||
import { hashNode, isNestedLeaf, isNode, isNodeLeaf, isSignerLeaf, isSubdigestLeaf, Leaf, WalletConfig, SignerLeaf, Topology, imageHash } from "./config" | ||
import { hashNode, isNestedLeaf, isNode, isNodeLeaf, isSignerLeaf, isSubdigestLeaf, Leaf, WalletConfig, SignerLeaf, Topology, imageHash, NodeLeaf, decodeSignerLeaf, isEncodedSignerLeaf } from "./config" | ||
import * as base from '../commons/signature' | ||
@@ -335,3 +335,3 @@ import { hashSetImageHash } from "./chained" | ||
if (config.threshold > 255) { | ||
if (ethers.BigNumber.from(config.threshold).gt(255)) { | ||
return { | ||
@@ -370,3 +370,6 @@ encoded: ethers.utils.solidityPack( | ||
if (trim && left.weight.eq(0) && right.weight.eq(0)) { | ||
const isLeftSigner = isSignerLeaf(topology.left) | ||
const isRightSigner = isSignerLeaf(topology.right) | ||
if (trim && left.weight.eq(0) && right.weight.eq(0) && !isLeftSigner && !isRightSigner) { | ||
return { | ||
@@ -380,3 +383,3 @@ // We don't need to include anything for this node | ||
if (trim && right.weight.eq(0)) { | ||
if (trim && right.weight.eq(0) && !isRightSigner) { | ||
return { | ||
@@ -393,3 +396,3 @@ // The right node doesn't have any weight | ||
if (trim && left.weight.eq(0)) { | ||
if (trim && left.weight.eq(0) && !isLeftSigner) { | ||
return { | ||
@@ -670,3 +673,3 @@ // The left node doesn't have any weight | ||
export function encodeSignature( | ||
decoded: UnrecoveredChainedSignature | ChainedSignature | UnrecoveredSignature | Signature | ethers.BytesLike | ||
decoded: UnrecoveredChainedSignature | ChainedSignature | UnrecoveredSignature | Signature | ethers.BytesLike, | ||
): string { | ||
@@ -686,3 +689,3 @@ if (ethers.utils.isBytesLike(decoded)) return ethers.utils.hexlify(decoded) | ||
case SignatureType.Legacy: | ||
if (body.threshold > 255) { | ||
if (ethers.BigNumber.from(body.threshold).gt(255)) { | ||
throw new Error(`Legacy signature threshold is too large: ${body.threshold} (max 255)`) | ||
@@ -817,2 +820,135 @@ } | ||
export function subdigestsOfDecoded(utopology: UnrecoveredTopology): string[] { | ||
if (isUnrecoveredNode(utopology)) { | ||
return [...subdigestsOfDecoded(utopology.left), ...subdigestsOfDecoded(utopology.right)] | ||
} | ||
if (isUnrecoveredNestedLeaf(utopology)) { | ||
return subdigestsOfDecoded(utopology.tree) | ||
} | ||
if (isSubdigestLeaf(utopology)) { | ||
return [utopology.subdigest] | ||
} | ||
return [] | ||
} | ||
export async function trimSignature(signature: string | UnrecoveredSignature): Promise<string> { | ||
const decoded = typeof signature === 'string' ? decodeSignature(signature) : signature | ||
if (isUnrecoveredChainedSignature(decoded)) { | ||
// We need to trim every suffix AND the main signature | ||
const trimmed = await Promise.all([ | ||
trimSignature({ ...decoded, suffix: undefined } as UnrecoveredSignature), | ||
...decoded.suffix.map((s) => trimSignature(s)) | ||
]) | ||
return encodeChain(trimmed[0], trimmed.slice(1)) | ||
} | ||
const { trimmed } = await trimUnrecoveredTree(decoded.decoded.tree) | ||
return encodeSignature({ ...decoded, decoded: { ...decoded.decoded, tree: trimmed }}) | ||
} | ||
export async function trimUnrecoveredTree(tree: UnrecoveredTopology, trimStaticDigest: boolean = true): Promise<{ | ||
weight: number, | ||
trimmed: UnrecoveredTopology | ||
}> { | ||
if (isUnrecoveredNode(tree)) { | ||
const [left, right] = await Promise.all([ | ||
trimUnrecoveredTree(tree.left), | ||
trimUnrecoveredTree(tree.right) | ||
]) | ||
if (left.weight === 0 && right.weight === 0) { | ||
try { | ||
// If both weights are 0 then it means we don't have any signatures yet | ||
// because of that, we should be able to "recover" the tree with any subdigest | ||
// and still get the valid node hash (there shouldn't be any signatures to verify) | ||
const recovered = await recoverTopology(tree, ethers.constants.HashZero, undefined as any) | ||
return { | ||
weight: 0, | ||
trimmed: { | ||
nodeHash: hashNode(recovered) | ||
} as NodeLeaf | ||
} | ||
} catch { | ||
// If something fails it's more likely because some signatures have sneaked in | ||
// in that case we should keep this node | ||
} | ||
} else { | ||
return { | ||
weight: left.weight + right.weight, | ||
trimmed: { | ||
left: left.trimmed, | ||
right: right.trimmed | ||
} as UnrecoveredNode | ||
} | ||
} | ||
} | ||
if (isUnrecoveredNestedLeaf(tree)) { | ||
const trimmed = await trimUnrecoveredTree(tree.tree) | ||
if (trimmed.weight === 0) { | ||
try { | ||
// If the nested leaf is empty, we can recover it with any subdigest | ||
// and still get the valid node hash (there shouldn't be any signatures to verify) | ||
const recovered = await recoverTopology(tree, ethers.constants.HashZero, undefined as any) | ||
return { | ||
weight: 0, | ||
trimmed: { | ||
nodeHash: hashNode(recovered) | ||
} as NodeLeaf | ||
} | ||
} catch { | ||
// If something fails it's more likely because some signatures have sneaked in | ||
// in that case we should keep this node | ||
} | ||
} | ||
return { | ||
weight: trimmed.weight, | ||
trimmed: { | ||
weight: tree.weight, | ||
threshold: tree.threshold, | ||
tree: trimmed.trimmed | ||
} as UnrecoveredNestedLeaf | ||
} | ||
} | ||
// Hash nodes can be encoded as signer leaves if they have a weight below | ||
// 256, most likely the are signer leaves wrongly encoded | ||
if (isNodeLeaf(tree) && isEncodedSignerLeaf(tree.nodeHash)) { | ||
return { | ||
weight: 0, | ||
trimmed: { | ||
...decodeSignerLeaf(tree.nodeHash), | ||
} as SignerLeaf | ||
} | ||
} | ||
if (isUnrecoveredSignatureLeaf(tree) || (isSignerLeaf(tree) && tree.signature !== undefined)) { | ||
return { | ||
weight: ethers.BigNumber.from(tree.weight).toNumber(), | ||
trimmed: tree | ||
} | ||
} | ||
if (!trimStaticDigest && isSubdigestLeaf(tree)) { | ||
return { | ||
weight: +Infinity, | ||
trimmed: tree | ||
} | ||
} | ||
return { | ||
weight: 0, | ||
trimmed: tree | ||
} | ||
} | ||
export const SignatureCoder: base.SignatureCoder< | ||
@@ -831,2 +967,6 @@ WalletConfig, | ||
trim: (data: string): Promise<string> => { | ||
return trimSignature(data) | ||
}, | ||
supportsNoChainId: true, | ||
@@ -833,0 +973,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
348793
9571
+ Added@0xsequence/abi@0.0.0-20230622143614(transitive)
- Removed@0xsequence/abi@0.0.0-20230621220927(transitive)