Created
September 21, 2024 04:11
-
-
Save btspoony/ef7167e961eb942418bd370db6f83ee0 to your computer and use it in GitHub Desktop.
Flow Backend Snippets
This file contains hidden or 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 FlowService from "./flow.service"; | |
// Here is the configuration file for fixes. | |
import flowJSON from "@/flow.json" assert { type: "json" }; | |
let _instance: FlowService; | |
export async function getFlowInstance(): Promise<FlowService> { | |
if (!_instance) { | |
_instance = new FlowService(flowJSON); | |
await _instance.onModuleInit(); | |
} | |
return _instance; | |
} |
This file contains hidden or 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 * as fcl from "@onflow/fcl"; | |
import type { TransactionStatus } from '@onflow/typedefs'; | |
export type NetworkType = "mainnet" | "testnet" | "emulator"; | |
let isGloballyInited = false; | |
let globallyPromise = null; | |
export default class FlowService { | |
public readonly network: NetworkType; | |
private readonly flowJSON: object; | |
/** | |
* Initialize the Flow SDK | |
*/ | |
constructor(flowJSON: object) { | |
this.network = | |
(import.meta.env.PUBLIC_FLOW_NETWORK as NetworkType) ?? "emulator"; | |
this.flowJSON = flowJSON; | |
} | |
async onModuleInit() { | |
if (isGloballyInited) return; | |
const cfg = fcl.config(); | |
// Required | |
await cfg.put("flow.network", this.network); | |
// Set the maximum of gas limit | |
await cfg.put("fcl.limit", 9999); | |
switch (this.network) { | |
case "mainnet": | |
await cfg.put( | |
"accessNode.api", | |
import.meta.env.INTERNAL_MAINNET_ENDPOINT ?? | |
"https://mainnet.onflow.org" | |
); | |
break; | |
case "testnet": | |
await cfg.put("accessNode.api", "https://testnet.onflow.org"); | |
break; | |
case "emulator": | |
await cfg.put("accessNode.api", "http://localhost:8888"); | |
break; | |
default: | |
throw new Error(`Unknown network: ${String(this.network)}`); | |
} | |
// Load Flow JSON | |
await cfg.load({ flowJSON: this.flowJSON }); | |
isGloballyInited = true; | |
} | |
/** | |
* Ensure the Flow SDK is initialized | |
*/ | |
private async ensureInited() { | |
if (isGloballyInited) return; | |
if (!globallyPromise) { | |
globallyPromise = this.onModuleInit(); | |
} | |
return await globallyPromise; | |
} | |
/** | |
* Get account information | |
*/ | |
async getAccount(addr: string): Promise<any> { | |
await this.ensureInited(); | |
return await fcl.send([fcl.getAccount(addr)]).then(fcl.decode); | |
} | |
/** | |
* General method of sending transaction | |
*/ | |
async sendTransaction( | |
code: string, | |
args: fcl.ArgumentFunction, | |
mainAuthz?: fcl.FclAuthorization, | |
extraAuthz?: fcl.FclAuthorization[] | |
) { | |
await this.ensureInited(); | |
if (typeof mainAuthz !== "undefined") { | |
return await fcl.mutate({ | |
cadence: code, | |
args: args, | |
proposer: mainAuthz, | |
payer: mainAuthz, | |
authorizations: | |
(extraAuthz?.length ?? 0) === 0 | |
? [mainAuthz] | |
: [mainAuthz, ...extraAuthz], | |
}); | |
} else { | |
return await fcl.mutate({ | |
cadence: code, | |
args: args, | |
}); | |
} | |
} | |
/** | |
* Get transaction status | |
*/ | |
async getTransactionStatus( | |
transactionId: string | |
): Promise<TransactionStatus> { | |
await this.ensureInited(); | |
return await fcl.tx(transactionId).onceExecuted(); | |
} | |
/** | |
* Get chain id | |
*/ | |
async getChainId() { | |
await this.ensureInited(); | |
return await fcl.getChainId(); | |
} | |
/** | |
* Send transaction with single authorization | |
*/ | |
async onceTransactionSealed( | |
transactionId: string | |
): Promise<TransactionStatus> { | |
await this.ensureInited(); | |
return fcl.tx(transactionId).onceSealed(); | |
} | |
/** | |
* Get block object | |
* @param blockId | |
*/ | |
async getBlockHeaderObject(blockId: string): Promise<fcl.BlockHeaderObject> { | |
await this.ensureInited(); | |
return await fcl | |
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | |
.send([fcl.getBlockHeader(), fcl.atBlockId(blockId)]) | |
.then(fcl.decode); | |
} | |
/** | |
* Send script | |
*/ | |
async executeScript<T>( | |
code: string, | |
args: fcl.ArgumentFunction, | |
defaultValue: T | |
): Promise<T> { | |
await this.ensureInited(); | |
try { | |
const queryResult = await fcl.query({ | |
cadence: code, | |
args, | |
}); | |
return (queryResult as T) ?? defaultValue; | |
} catch (e) { | |
console.error(e); | |
return defaultValue; | |
} | |
} | |
} |
This file contains hidden or 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 elliptic from 'elliptic'; | |
import * as fcl from '@onflow/fcl'; | |
import type { CompositeSignature } from '@onflow/typedefs'; | |
import { getFlowInstance } from './flow.service.factory'; | |
import PureSigner from './pure.signer'; | |
const ec = new elliptic.ec('p256'); | |
export default class FlowSigner { | |
constructor( | |
public readonly address: string, | |
private readonly privateKeyHex?: string | |
) {} | |
/** | |
* Send a transaction | |
* @param code Cadence code | |
* @param args Cadence arguments | |
*/ | |
async sendTransaction( | |
code: string, | |
args: fcl.ArgumentFunction, | |
authz?: fcl.FclAuthorization | |
) { | |
const flowService = await getFlowInstance(); | |
return await flowService.sendTransaction( | |
code, | |
args, | |
authz ?? this.buildAuthorization() | |
); | |
} | |
/** | |
* Execute a script | |
* @param code Cadence code | |
* @param args Cadence arguments | |
*/ | |
async executeScript<T>( | |
code: string, | |
args: fcl.ArgumentFunction, | |
defaultValue: T | |
): Promise<T> { | |
const flowService = await getFlowInstance(); | |
return await flowService.executeScript(code, args, defaultValue); | |
} | |
/** | |
* Build authorization | |
*/ | |
buildAuthorization(accountIndex = 0, privateKey = this.privateKeyHex) { | |
const address = this.address; | |
if (!privateKey) { | |
throw new Error('No private key provided'); | |
} | |
return (account: any) => { | |
return { | |
...account, | |
tempId: `${address}-${accountIndex}`, | |
addr: fcl.sansPrefix(address), | |
keyId: Number(accountIndex), | |
signingFunction: (signable: any): Promise<CompositeSignature> => { | |
return Promise.resolve({ | |
f_type: 'CompositeSignature', | |
f_vsn: '1.0.0', | |
addr: fcl.withPrefix(address), | |
keyId: Number(accountIndex), | |
signature: this.signMessage(signable.message, privateKey), | |
}); | |
}, | |
}; | |
}; | |
} | |
/** | |
* Sign a message | |
* @param message Message to sign | |
*/ | |
signMessage(message: string, privateKey = this.privateKeyHex) { | |
return PureSigner.signWithKey(privateKey, message); | |
} | |
} |
This file contains hidden or 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 elliptic from "elliptic"; | |
import { SHA3 } from "sha3"; | |
export default class PureSigner { | |
/** | |
* Sign a message with a private key | |
*/ | |
static signWithKey(privateKeyHex: string, msg: string) { | |
const ec = new elliptic.ec("p256"); | |
const key = ec.keyFromPrivate(Buffer.from(privateKeyHex, "hex")); | |
const sig = key.sign(this._hashMsg(msg)); | |
const n = 32; | |
const r = sig.r.toArrayLike(Buffer, "be", n); | |
const s = sig.s.toArrayLike(Buffer, "be", n); | |
return Buffer.concat([r.valueOf(), s.valueOf()]).toString('hex'); | |
} | |
/** | |
* Hash a message | |
*/ | |
private static _hashMsg(msg: string) { | |
const sha = new SHA3(256); | |
sha.update(Buffer.from(msg, "hex")); | |
return sha.digest(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment