Encode message for signing

This commit is contained in:
Andrew Morris
2023-05-13 20:54:30 +01:00
parent 36d2519c7c
commit 8d0dbf9c91
6 changed files with 653 additions and 7 deletions

3
contracts-bits-n-bobs/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
artifacts
cache
typechain-types

View File

@@ -0,0 +1,342 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
PromiseOrValue,
} from "./common";
export interface IERC20Interface extends utils.Interface {
functions: {
"allowance(address,address)": FunctionFragment;
"approve(address,uint256)": FunctionFragment;
"balanceOf(address)": FunctionFragment;
"totalSupply()": FunctionFragment;
"transfer(address,uint256)": FunctionFragment;
"transferFrom(address,address,uint256)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "allowance"
| "approve"
| "balanceOf"
| "totalSupply"
| "transfer"
| "transferFrom"
): FunctionFragment;
encodeFunctionData(
functionFragment: "allowance",
values: [PromiseOrValue<string>, PromiseOrValue<string>]
): string;
encodeFunctionData(
functionFragment: "approve",
values: [PromiseOrValue<string>, PromiseOrValue<BigNumberish>]
): string;
encodeFunctionData(
functionFragment: "balanceOf",
values: [PromiseOrValue<string>]
): string;
encodeFunctionData(
functionFragment: "totalSupply",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "transfer",
values: [PromiseOrValue<string>, PromiseOrValue<BigNumberish>]
): string;
encodeFunctionData(
functionFragment: "transferFrom",
values: [
PromiseOrValue<string>,
PromiseOrValue<string>,
PromiseOrValue<BigNumberish>
]
): string;
decodeFunctionResult(functionFragment: "allowance", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferFrom",
data: BytesLike
): Result;
events: {
"Approval(address,address,uint256)": EventFragment;
"Transfer(address,address,uint256)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment;
}
export interface ApprovalEventObject {
owner: string;
spender: string;
value: BigNumber;
}
export type ApprovalEvent = TypedEvent<
[string, string, BigNumber],
ApprovalEventObject
>;
export type ApprovalEventFilter = TypedEventFilter<ApprovalEvent>;
export interface TransferEventObject {
from: string;
to: string;
value: BigNumber;
}
export type TransferEvent = TypedEvent<
[string, string, BigNumber],
TransferEventObject
>;
export type TransferEventFilter = TypedEventFilter<TransferEvent>;
export interface IERC20 extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: IERC20Interface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
allowance(
owner: PromiseOrValue<string>,
spender: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<[BigNumber]>;
approve(
spender: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
balanceOf(
account: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<[BigNumber]>;
totalSupply(overrides?: CallOverrides): Promise<[BigNumber]>;
transfer(
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
transferFrom(
from: PromiseOrValue<string>,
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
};
allowance(
owner: PromiseOrValue<string>,
spender: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<BigNumber>;
approve(
spender: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
balanceOf(
account: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<BigNumber>;
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
transfer(
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
transferFrom(
from: PromiseOrValue<string>,
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
callStatic: {
allowance(
owner: PromiseOrValue<string>,
spender: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<BigNumber>;
approve(
spender: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<boolean>;
balanceOf(
account: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<BigNumber>;
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
transfer(
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<boolean>;
transferFrom(
from: PromiseOrValue<string>,
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<boolean>;
};
filters: {
"Approval(address,address,uint256)"(
owner?: PromiseOrValue<string> | null,
spender?: PromiseOrValue<string> | null,
value?: null
): ApprovalEventFilter;
Approval(
owner?: PromiseOrValue<string> | null,
spender?: PromiseOrValue<string> | null,
value?: null
): ApprovalEventFilter;
"Transfer(address,address,uint256)"(
from?: PromiseOrValue<string> | null,
to?: PromiseOrValue<string> | null,
value?: null
): TransferEventFilter;
Transfer(
from?: PromiseOrValue<string> | null,
to?: PromiseOrValue<string> | null,
value?: null
): TransferEventFilter;
};
estimateGas: {
allowance(
owner: PromiseOrValue<string>,
spender: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<BigNumber>;
approve(
spender: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
balanceOf(
account: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<BigNumber>;
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
transfer(
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
transferFrom(
from: PromiseOrValue<string>,
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
};
populateTransaction: {
allowance(
owner: PromiseOrValue<string>,
spender: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
approve(
spender: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
balanceOf(
account: PromiseOrValue<string>,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
totalSupply(overrides?: CallOverrides): Promise<PopulatedTransaction>;
transfer(
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
transferFrom(
from: PromiseOrValue<string>,
to: PromiseOrValue<string>,
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
};
}

View File

@@ -0,0 +1,206 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type {
IERC20,
IERC20Interface,
} from "./IERC20";
const _abi = [
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Approval",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Transfer",
type: "event",
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
{
internalType: "address",
name: "spender",
type: "address",
},
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "spender",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "approve",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "account",
type: "address",
},
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "to",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "transfer",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "from",
type: "address",
},
{
internalType: "address",
name: "to",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "transferFrom",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
] as const;
export class IERC20__factory {
static readonly abi = _abi;
static createInterface(): IERC20Interface {
return new utils.Interface(_abi) as IERC20Interface;
}
static connect(address: string, signerOrProvider: Signer | Provider): IERC20 {
return new Contract(address, _abi, signerOrProvider) as IERC20;
}
}

View File

@@ -0,0 +1,46 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type { Listener } from "@ethersproject/providers";
import type { Event, EventFilter } from "ethers";
export interface TypedEvent<
TArgsArray extends Array<any> = any,
TArgsObject = any
> extends Event {
args: TArgsArray & TArgsObject;
}
export interface TypedEventFilter<_TEvent extends TypedEvent>
extends EventFilter {}
export interface TypedListener<TEvent extends TypedEvent> {
(...listenerArg: [...__TypechainArgsArray<TEvent>, TEvent]): void;
}
type __TypechainArgsArray<T> = T extends TypedEvent<infer U> ? U : never;
export interface OnEvent<TRes> {
<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>,
listener: TypedListener<TEvent>
): TRes;
(eventName: string, listener: Listener): TRes;
}
export type MinEthersFactory<C, ARGS> = {
deploy(...a: ARGS[]): Promise<C>;
};
export type GetContractTypeFromFactory<F> = F extends MinEthersFactory<
infer C,
any
>
? C
: never;
export type GetARGsTypeFromFactory<F> = F extends MinEthersFactory<any, any>
? Parameters<F["deploy"]>
: never;
export type PromiseOrValue<T> = T | Promise<T>;

View File

@@ -113,10 +113,24 @@ export default class AppContext {
console.log('tx complete');
}
async addSignature(paymentChannel: PaymentChannel, payment: Payment) {
const encodedPayment = PaymentChannel.encodePayment(payment);
createPayment(
simplePayment: Pick<Payment, 'to' | 'amount' | 'description'>,
): Payment {
return {
sender: this.address,
nonce: '0x00', // TODO: nonce
token: '0x0000000000000000000000000000000000000000', // TODO: token
...simplePayment,
};
}
const signature = this.signer.sign(ethers.utils.hexlify(encodedPayment));
async addSignature(paymentChannel: PaymentChannel, payment: Payment) {
const encodedPayment = await PaymentChannel.encodePayment(
payment,
this.aaProvider,
);
const signature = this.signer.sign(encodedPayment);
await paymentChannel.addSignature(this.signer.pubkey, signature);
}

View File

@@ -3,11 +3,21 @@ import * as io from 'io-ts';
import { signer } from '@thehubbleproject/bls';
import Channel from '../utils/Channel';
import assertType from '../utils/assertType';
import { ERC4337EthersProvider } from '@account-abstraction/sdk';
import { IERC20__factory } from '../ERC20/IERC20__factory';
const callGasLimit = '1000000';
const verificationGasLimit = '1000000';
const preVerificationGas = '1000000';
const maxFeePerGas = '100000000000'; // 100 gwei
const maxPriorityFeePerGas = '1000000000'; // 1 gwei
export const Payment = io.type({
sender: io.string,
nonce: io.string,
token: io.string,
to: io.string,
amount: io.number,
amount: io.string,
description: io.string,
});
@@ -78,8 +88,33 @@ export default class PaymentChannel {
};
}
static encodePayment(payment: Payment) {
// TODO: Get the actual message that needs to be signed
return new TextEncoder().encode(JSON.stringify(payment));
static async encodePayment(
payment: Payment,
aaProvider: ERC4337EthersProvider,
) {
const callData = await aaProvider.smartAccountAPI.encodeExecute(
payment.token,
0,
IERC20__factory.createInterface().encodeFunctionData('transfer', [
payment.to,
payment.amount,
]),
);
const userOpHash = await aaProvider.smartAccountAPI.getUserOpHash({
sender: payment.sender,
nonce: payment.nonce,
initCode: '0x',
callData,
callGasLimit,
verificationGasLimit,
preVerificationGas,
maxFeePerGas,
maxPriorityFeePerGas,
paymasterAndData: '0x',
signature: '0x',
});
return userOpHash;
}
}