feat: fetch options plugin (#255)
This commit is contained in:
19
README.md
19
README.md
@@ -588,6 +588,25 @@ An example - LMDB - implementation is available [here](https://github.com/warp-c
|
|||||||
|
|
||||||
A dedicated CLI which eases the process of using main methods of the Warp SDK library has been created. Please refer to [`warp-contracts-cli` npm page](https://www.npmjs.com/package/warp-contracts-cli) for more details.
|
A dedicated CLI which eases the process of using main methods of the Warp SDK library has been created. Please refer to [`warp-contracts-cli` npm page](https://www.npmjs.com/package/warp-contracts-cli) for more details.
|
||||||
|
|
||||||
|
### Customize `fetch` options
|
||||||
|
|
||||||
|
It is possible to customize `fetch` options using dedicated plugin. In order to change `fetch` options one needs to create an implementation of [WarpPlugin](https://github.com/warp-contracts/warp/blob/main/src/core/WarpPlugin.ts) interface. `process` method will receive following properties:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface FetchRequest {
|
||||||
|
input: RequestInfo | URL;
|
||||||
|
init: Partial<RequestInit>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
...and it should return updated `fetch` options (by returning updated `init` object). An example of such implementation in [src/tools/fetch-options-plugin.ts](https://github.com/warp-contracts/warp/tree/main/tools/fetch-options-plugin.ts).
|
||||||
|
|
||||||
|
In order to use this plugin, it needs to be attached while creating `Warp` instance, e.g.:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const warp = WarpFactory.forMainnet().use(new FetchOptionsPlugin());
|
||||||
|
```
|
||||||
|
|
||||||
### Migrations
|
### Migrations
|
||||||
|
|
||||||
#### old factories to WarpFactory
|
#### old factories to WarpFactory
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import { generateMockVrf } from '../utils/vrf';
|
|||||||
import { Signature, SignatureType } from './Signature';
|
import { Signature, SignatureType } from './Signature';
|
||||||
import { ContractDefinition } from '../core/ContractDefinition';
|
import { ContractDefinition } from '../core/ContractDefinition';
|
||||||
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
||||||
|
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link Contract} that is backwards compatible with current style
|
* An implementation of {@link Contract} that is backwards compatible with current style
|
||||||
@@ -59,6 +60,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
private _sorter: InteractionsSorter;
|
private _sorter: InteractionsSorter;
|
||||||
private _rootSortKey: string;
|
private _rootSortKey: string;
|
||||||
private signature: Signature;
|
private signature: Signature;
|
||||||
|
private warpFetchWrapper: WarpFetchWrapper;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _contractTxId: string,
|
private readonly _contractTxId: string,
|
||||||
@@ -113,6 +115,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.getCallStack = this.getCallStack.bind(this);
|
this.getCallStack = this.getCallStack.bind(this);
|
||||||
|
this.warpFetchWrapper = new WarpFetchWrapper(this.warp);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readState(
|
async readState(
|
||||||
@@ -301,15 +304,16 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
options.vrf
|
options.vrf
|
||||||
);
|
);
|
||||||
|
|
||||||
const response = await fetch(`${this._evaluationOptions.sequencerUrl}gateway/sequencer/register`, {
|
const response = await this.warpFetchWrapper
|
||||||
method: 'POST',
|
.fetch(`${this._evaluationOptions.sequencerUrl}gateway/sequencer/register`, {
|
||||||
body: JSON.stringify(interactionTx),
|
method: 'POST',
|
||||||
headers: {
|
body: JSON.stringify(interactionTx),
|
||||||
'Accept-Encoding': 'gzip, deflate, br',
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
Accept: 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
Accept: 'application/json'
|
||||||
})
|
}
|
||||||
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.logger.debug(res);
|
this.logger.debug(res);
|
||||||
return res.ok ? res.json() : Promise.reject(res);
|
return res.ok ? res.json() : Promise.reject(res);
|
||||||
@@ -746,12 +750,13 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
|
|
||||||
async syncState(externalUrl: string, params?: any): Promise<Contract> {
|
async syncState(externalUrl: string, params?: any): Promise<Contract> {
|
||||||
const { stateEvaluator } = this.warp;
|
const { stateEvaluator } = this.warp;
|
||||||
const response = await fetch(
|
const response = await this.warpFetchWrapper
|
||||||
`${externalUrl}?${new URLSearchParams({
|
.fetch(
|
||||||
id: this._contractTxId,
|
`${externalUrl}?${new URLSearchParams({
|
||||||
...params
|
id: this._contractTxId,
|
||||||
})}`
|
...params
|
||||||
)
|
})}`
|
||||||
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.ok ? res.json() : Promise.reject(res);
|
return res.ok ? res.json() : Promise.reject(res);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,11 +2,20 @@
|
|||||||
import Arweave from 'arweave';
|
import Arweave from 'arweave';
|
||||||
import Transaction from 'arweave/node/lib/transaction';
|
import Transaction from 'arweave/node/lib/transaction';
|
||||||
import { Signature, SignatureType } from '../../../contract/Signature';
|
import { Signature, SignatureType } from '../../../contract/Signature';
|
||||||
|
import { WarpFetchWrapper } from '../../../core/WarpFetchWrapper';
|
||||||
import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
|
import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
|
||||||
import { Warp } from '../../../core/Warp';
|
import { Warp } from '../../../core/Warp';
|
||||||
import { WARP_GW_URL } from '../../../core/WarpFactory';
|
import { WARP_GW_URL } from '../../../core/WarpFactory';
|
||||||
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
||||||
import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData, ArWallet, BundlrNodeType, BUNDLR_NODES } from '../CreateContract';
|
import {
|
||||||
|
CreateContract,
|
||||||
|
ContractData,
|
||||||
|
ContractDeploy,
|
||||||
|
FromSrcTxContractData,
|
||||||
|
ArWallet,
|
||||||
|
BundlrNodeType,
|
||||||
|
BUNDLR_NODES
|
||||||
|
} from '../CreateContract';
|
||||||
import { SourceData, SourceImpl } from './SourceImpl';
|
import { SourceData, SourceImpl } from './SourceImpl';
|
||||||
import { Buffer } from 'redstone-isomorphic';
|
import { Buffer } from 'redstone-isomorphic';
|
||||||
|
|
||||||
@@ -15,10 +24,12 @@ export class DefaultCreateContract implements CreateContract {
|
|||||||
private readonly source: SourceImpl;
|
private readonly source: SourceImpl;
|
||||||
|
|
||||||
private signature: Signature;
|
private signature: Signature;
|
||||||
|
private readonly warpFetchWrapper: WarpFetchWrapper;
|
||||||
|
|
||||||
constructor(private readonly arweave: Arweave, private warp: Warp) {
|
constructor(private readonly arweave: Arweave, private warp: Warp) {
|
||||||
this.deployFromSourceTx = this.deployFromSourceTx.bind(this);
|
this.deployFromSourceTx = this.deployFromSourceTx.bind(this);
|
||||||
this.source = new SourceImpl(this.warp);
|
this.source = new SourceImpl(this.warp);
|
||||||
|
this.warpFetchWrapper = new WarpFetchWrapper(this.warp);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||||
@@ -190,7 +201,7 @@ export class DefaultCreateContract implements CreateContract {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${WARP_GW_URL}/gateway/contracts/deploy`, {
|
const response = await this.warpFetchWrapper.fetch(`${WARP_GW_URL}/gateway/contracts/deploy`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
37
src/core/WarpFetchWrapper.ts
Normal file
37
src/core/WarpFetchWrapper.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { LoggerFactory } from '../logging/LoggerFactory';
|
||||||
|
import { Warp } from './Warp';
|
||||||
|
|
||||||
|
export interface FetchRequest {
|
||||||
|
input: RequestInfo | URL;
|
||||||
|
init: Partial<RequestInit>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WarpFetchWrapper {
|
||||||
|
private readonly name = 'WarpFetchWrapper';
|
||||||
|
private readonly logger = LoggerFactory.INST.create(this.name);
|
||||||
|
|
||||||
|
constructor(private warp: Warp) {
|
||||||
|
this.warp = warp;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||||
|
let fetchOptions: RequestInit;
|
||||||
|
|
||||||
|
if (this.warp.hasPlugin('fetch-options')) {
|
||||||
|
const fetchOptionsPlugin = this.warp.loadPlugin<FetchRequest, Partial<RequestInit>>('fetch-options');
|
||||||
|
try {
|
||||||
|
const updatedFetchOptions = fetchOptionsPlugin.process({ input, init: init || {} });
|
||||||
|
fetchOptions = { ...init, ...updatedFetchOptions };
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message) {
|
||||||
|
this.logger.error(e.message);
|
||||||
|
}
|
||||||
|
throw new Error(`Unable to process fetch options: ${e.message}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fetchOptions = init;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(input, fetchOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ export const knownWarpPlugins = [
|
|||||||
'smartweave-extension-ethers',
|
'smartweave-extension-ethers',
|
||||||
'subscription',
|
'subscription',
|
||||||
'ivm-handler-api',
|
'ivm-handler-api',
|
||||||
'evaluation-progress'
|
'evaluation-progress',
|
||||||
|
'fetch-options'
|
||||||
] as const;
|
] as const;
|
||||||
export type WarpPluginType = typeof knownWarpPlugins[number];
|
export type WarpPluginType = typeof knownWarpPlugins[number];
|
||||||
|
|
||||||
|
|||||||
70
tools/fetch-options-plugin.ts
Normal file
70
tools/fetch-options-plugin.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { WarpPlugin, WarpPluginType } from '../src/core/WarpPlugin';
|
||||||
|
import { FetchRequest } from '../src/core/WarpFetchWrapper';
|
||||||
|
import { JWKInterface } from 'arweave/node/lib/wallet';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { LoggerFactory } from '../src/logging/LoggerFactory';
|
||||||
|
import { defaultCacheOptions, WarpFactory } from '../src/core/WarpFactory';
|
||||||
|
|
||||||
|
class FetchOptionsPlugin implements WarpPlugin<FetchRequest, RequestInit> {
|
||||||
|
process(request: FetchRequest): Partial<RequestInit> {
|
||||||
|
const url = request.input;
|
||||||
|
|
||||||
|
let fetchOptions: Partial<RequestInit> = {};
|
||||||
|
|
||||||
|
if (url == `https://d1o5nlqr4okus2.cloudfront.net/gateway/sequencer/register`) {
|
||||||
|
fetchOptions = {
|
||||||
|
keepalive: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
type(): WarpPluginType {
|
||||||
|
return 'fetch-options';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const wallet: JWKInterface = readJSON('./.secrets/jwk.json');
|
||||||
|
LoggerFactory.INST.logLevel('debug');
|
||||||
|
const logger = LoggerFactory.INST.create('FetchOptionsPlugin');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const warp = WarpFactory.forMainnet({ ...defaultCacheOptions, inMemory: true }).use(new FetchOptionsPlugin());
|
||||||
|
|
||||||
|
const jsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.js'), 'utf8');
|
||||||
|
const initialState = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.json'), 'utf8');
|
||||||
|
|
||||||
|
const { contractTxId } = await warp.createContract.deploy({
|
||||||
|
wallet,
|
||||||
|
initState: initialState,
|
||||||
|
src: jsContractSrc
|
||||||
|
});
|
||||||
|
|
||||||
|
const contract = warp.contract(contractTxId).connect(wallet);
|
||||||
|
|
||||||
|
await contract.writeInteraction({
|
||||||
|
function: 'transfer',
|
||||||
|
target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M',
|
||||||
|
qty: 55555
|
||||||
|
});
|
||||||
|
|
||||||
|
const { cachedValue } = await contract.readState();
|
||||||
|
logger.info(`Cached value: ${cachedValue}`);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readJSON(path: string): JWKInterface {
|
||||||
|
const content = fs.readFileSync(path, 'utf-8');
|
||||||
|
try {
|
||||||
|
return JSON.parse(content);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`File "${path}" does not contain a valid JSON`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((e) => console.error(e));
|
||||||
Reference in New Issue
Block a user