iso-filecoin
Version:
Isomorphic filecoin abstractions for RPC, signatures, address, token and wallet
212 lines (185 loc) • 5.21 kB
JavaScript
'use strict';
var index = require('../node_modules/.pnpm/@ipld_dag-cbor@9.2.4/node_modules/@ipld/dag-cbor/src/index.cjs');
var rfc4648 = require('../node_modules/.pnpm/iso-base@4.1.0/node_modules/iso-base/src/rfc4648.cjs');
var src_address = require('./address.cjs');
var src_token = require('./token.cjs');
var src_utils = require('./utils.cjs');
var schemas = require('../node_modules/.pnpm/zod@4.0.10/node_modules/zod/v4/classic/schemas.cjs');
/**
* @typedef {import('./types.js').MessageObj} MessageObj
* @typedef {import('./types.js').PartialMessageObj} PartialMessageObj
*/
/**
* Message validation schema
*/
const MessageSchema = schemas.object({
version: schemas.literal(0).default(0),
nonce: schemas.int().nonnegative().default(0),
gasLimit: schemas.int().nonnegative().default(0),
gasFeeCap: schemas.string().default('0'),
gasPremium: schemas.string().default('0'),
method: schemas.int().nonnegative().default(0),
to: schemas.string(),
from: schemas.string(),
/**
* Value in attoFIL
*/
value: schemas.string()
.min(1)
.refine((v) => !v.startsWith('-'), {
message: 'value must not be negative',
}),
/**
* Params encoded as base64pad
*/
params: schemas.base64().default(''),
});
const MessageSchemaPartial = MessageSchema.partial({
version: true,
nonce: true,
gasLimit: true,
gasFeeCap: true,
gasPremium: true,
method: true,
params: true,
});
const Schemas = {
message: MessageSchema,
messagePartial: MessageSchemaPartial,
};
/**
* Filecoin Message class
*/
class Message {
/** @type {Uint8Array | undefined} */
#bytes
/** @type {Uint8Array | undefined} */
#cidBytes
/**
*
* @param {PartialMessageObj} msg
*/
constructor(msg) {
const _msg = MessageSchema.parse(msg);
this.version = _msg.version;
this.to = _msg.to;
this.from = _msg.from;
this.nonce = _msg.nonce;
this.value = _msg.value;
this.gasLimit = _msg.gasLimit;
this.gasFeeCap = _msg.gasFeeCap;
this.gasPremium = _msg.gasPremium;
this.method = _msg.method;
this.params = _msg.params;
}
/**
* Convert message to Lotus message
*/
toLotus() {
return {
Version: this.version,
To: this.to,
From: this.from,
Nonce: this.nonce,
Value: this.value,
GasLimit: this.gasLimit,
GasFeeCap: this.gasFeeCap,
GasPremium: this.gasPremium,
Method: this.method,
Params: this.params,
}
}
/**
* Create message from Lotus message
*
* @param {import('./types').LotusMessage} json
*/
static fromLotus(json) {
/** @type {MessageObj} */
const obj = {
version: json.Version,
to: json.To,
from: json.From,
nonce: json.Nonce,
value: json.Value,
gasLimit: json.GasLimit,
gasFeeCap: json.GasFeeCap,
gasPremium: json.GasPremium,
method: json.Method,
params: json.Params,
};
return new Message(obj)
}
/**
* Prepare message for signing with nonce and gas estimation
*
* @param {import('./rpc.js').RPC} rpc
*/
async prepare(rpc) {
if (src_address.isIdMaskAddress(this.to)) {
throw new Error('ID mask addresses are not supported for recipient')
}
const parsedTo = src_address.from(this.to, rpc.network);
// Change to method to InvokeEVM for Bare value sends to 0x address to avoid losing funds
// @see https://docs.filecoin.io/smart-contracts/filecoin-evm-runtime/difference-with-ethereum#bare-value-sends
if (src_address.isAddressDelegated(parsedTo) && this.method === 0) {
this.method = 3844450837;
}
this.to = parsedTo.toString();
if (this.nonce === 0) {
const nonce = await rpc.nonce(this.from);
if (nonce.error) {
throw new Error(nonce.error.message)
}
this.nonce = nonce.result;
}
if (
(this.gasLimit === 0 && this.gasFeeCap === '0') ||
this.gasPremium === '0'
) {
const gas = await rpc.gasEstimate({ msg: this });
if (gas.error) {
throw new Error(gas.error.message)
}
this.gasLimit = gas.result.GasLimit;
this.gasFeeCap = gas.result.GasFeeCap;
this.gasPremium = gas.result.GasPremium;
}
return this
}
/**
* Serialize message using dag-cbor
*/
serialize() {
if (this.#bytes) {
return this.#bytes
}
const msg = [
this.version,
src_address.fromString(this.to).toBytes(),
src_address.fromString(this.from).toBytes(),
this.nonce,
src_token.Token.fromAttoFIL(this.value).toBytes(),
this.gasLimit,
src_token.Token.fromAttoFIL(this.gasFeeCap).toBytes(),
src_token.Token.fromAttoFIL(this.gasPremium).toBytes(),
this.method,
rfc4648.base64pad.decode(this.params),
];
this.#bytes = /** @type {Uint8Array}*/ (index.encode(msg));
return this.#bytes
}
/**
* CID bytes of the filecoin message
*/
cidBytes() {
if (this.#cidBytes) {
return this.#cidBytes
}
this.#cidBytes = src_utils.lotusCid(this.serialize());
return this.#cidBytes
}
}
exports.Message = Message;
exports.MessageSchema = MessageSchema;
exports.Schemas = Schemas;