viem
Version:
89 lines • 3.63 kB
JavaScript
import * as Address from 'ox/Address';
import * as Hash from 'ox/Hash';
import * as Json from 'ox/Json';
import { MultisigConfig } from 'ox/tempo';
import { BaseError } from '../../errors/base.js';
import { nativeMultisigFactory } from '../Addresses.js';
/** A valid 48-owner config is much smaller; 64 KiB bounds hostile store parsing work. */
const maxStoredValueLength = 65_536;
/** Zero commitment used before an account is initialized. */
const zeroCommitment = `0x${'00'.repeat(32)}`;
/** Reads a cached multisig config. */
export async function read(store, options) {
const { address, commitment } = options;
const value = await store.getItem(key({ address, commitment }));
if (value === null || value === undefined)
return null;
const config = deserialize(value);
assertKey({ address, commitment, config });
return config;
}
/** Writes a validated multisig config. */
export async function write(store, options) {
const { address, commitment } = options;
const config = MultisigConfig.from(options.config);
assertKey({ address, commitment, config });
const value = serialize(config);
await store.setItem(key({ address, commitment }), value);
// Initialization commits the version-zero config without changing its version.
if (commitment.toLowerCase() === zeroCommitment)
await store.setItem(key({ address, commitment: MultisigConfig.getCommitment(config) }), value);
}
/** Verifies that a config matches its account-and-commitment key. */
function assertKey(options) {
const { address, commitment, config } = options;
if (!Address.validate(address) || !Hash.validate(commitment))
throw new InvalidStoreValueError();
if (config.version === 0n) {
if (!Address.isEqual(MultisigConfig.getAddress(config, { factory: nativeMultisigFactory }), address))
throw new InvalidStoreValueError();
if (commitment.toLowerCase() === zeroCommitment)
return;
}
if (MultisigConfig.getCommitment(config).toLowerCase() !==
commitment.toLowerCase())
throw new InvalidStoreValueError();
}
/** Deserializes a multisig config from storage. */
function deserialize(value) {
try {
if (value.length > maxStoredValueLength)
throw new InvalidStoreValueError();
return MultisigConfig.fromRpc(Json.parse(value));
}
catch (cause) {
if (cause instanceof InvalidStoreValueError)
throw cause;
throw new InvalidStoreValueError({ cause });
}
}
/** Returns the store key for an account and config commitment. */
function key(options) {
const { address, commitment } = options;
return `multisig:config:${address.toLowerCase()}:${commitment.toLowerCase()}`;
}
/** Serializes a multisig config for storage. */
function serialize(config) {
try {
const value = Json.stringify(MultisigConfig.toRpc(config));
if (value.length > maxStoredValueLength)
throw new InvalidStoreValueError();
return value;
}
catch (cause) {
if (cause instanceof InvalidStoreValueError)
throw cause;
throw new InvalidStoreValueError({ cause });
}
}
/** Thrown when a stored multisig config is malformed or mismatched. */
export class InvalidStoreValueError extends BaseError {
/** Creates an invalid store value error. */
constructor(options = {}) {
super('Stored multisig config is malformed or mismatched.', {
cause: options.cause,
name: 'Multisig.Config.InvalidStoreValueError',
});
}
}
//# sourceMappingURL=Config.js.map