diff --git a/examples/eas-relayer-signer/package.json b/examples/eas-relayer-signer/package.json new file mode 100644 index 00000000..020e6cd7 --- /dev/null +++ b/examples/eas-relayer-signer/package.json @@ -0,0 +1,23 @@ +{ + "name": "@openzeppelin/defender-sdk-example-eas-relay-signer", + "version": "1.15.2", + "private": true, + "main": "index.js", + "type": "module", + "scripts": { + "build": "tsc", + "start": "node dist/index.js" + }, + "author": "OpenZeppelin Defender ", + "license": "MIT", + "description": "An example of using the Ethereum Attestation Service with a Defender Relay Signer", + "devDependencies": { + "typescript": "^5.6.3" + }, + "dependencies": { + "@ethereum-attestation-service/eas-sdk": "^2.7.0", + "@openzeppelin/defender-sdk": "1.15.2", + "dotenv": "^16.4.5", + "ethers": "^6.13.4" + } +} diff --git a/examples/eas-relayer-signer/src/index.ts b/examples/eas-relayer-signer/src/index.ts new file mode 100644 index 00000000..abcd954e --- /dev/null +++ b/examples/eas-relayer-signer/src/index.ts @@ -0,0 +1,57 @@ +import { EAS, NO_EXPIRATION, SchemaEncoder, TransactionSigner } from '@ethereum-attestation-service/eas-sdk'; + +import dotenv from 'dotenv'; +dotenv.config(); +import { Agent } from 'node:https'; +import process from 'node:process'; + +import { Defender, DefenderOptions } from '@openzeppelin/defender-sdk'; +import { TypeDataSigner } from '@ethereum-attestation-service/eas-sdk/dist/offchain/typed-data-handler.js'; + +export const EASContractAddress = '0xC2679fBD37d54388Ce493F1DB75320D236e1815e'; // Sepolia v0.26 + +const creds = { + relayerApiKey: process.env.RELAYER_API_KEY, + relayerApiSecret: process.env.RELAYER_API_SECRET, + //optional https config to keep connection alive. You can pass any configs that are accepted by https.Agent + httpsAgent: new Agent({ keepAlive: true }), +}; + +const validForSeconds = 60 * 60 * 24; +const client = new Defender(creds as DefenderOptions); +const provider = client.relaySigner.getProvider({ ethersVersion: 'v6' }); +const signer = await client.relaySigner.getSigner(provider, { + speed: 'fast', + validForSeconds, + ethersVersion: 'v6', +}); + +// Initialize EAS with the EAS contract address on whichever chain where your schema is defined +const eas = new EAS(EASContractAddress); +eas.connect(provider as unknown as TransactionSigner); + +const offchain = await eas.getOffchain(); + +// Initialize SchemaEncoder with the schema string +// Note these values are sample values and should be filled with actual values +// Code samples can be found when viewing each schema on easscan.org +const schemaEncoder = new SchemaEncoder('uint256 eventId, uint8 voteIndex'); +const encodedData = schemaEncoder.encodeData([ + { name: 'eventId', value: 1, type: 'uint256' }, + { name: 'voteIndex', value: 1, type: 'uint8' }, +]); + +const offchainAttestation = await offchain.signOffchainAttestation( + { + recipient: '0xFD50b031E778fAb33DfD2Fc3Ca66a1EeF0652165', + expirationTime: NO_EXPIRATION, // Unix timestamp of when attestation expires (0 for no expiration) + time: BigInt(Math.floor(Date.now() / 1000)), // Unix timestamp of current time + revocable: true, // Be aware that if your schema is not revocable, this MUST be false + schema: '0xb16fa048b0d597f5a821747eba64efa4762ee5143e9a80600d0005386edfc995', + refUID: '0x0000000000000000000000000000000000000000000000000000000000000000', + data: encodedData, + }, + signer as TypeDataSigner, +); + +console.log(offchainAttestation); diff --git a/examples/eas-relayer-signer/tsconfig.json b/examples/eas-relayer-signer/tsconfig.json new file mode 100644 index 00000000..ab09572e --- /dev/null +++ b/examples/eas-relayer-signer/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + /* Language and Environment */ + "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + + /* Modules */ + "module": "nodenext" /* Specify what module code is generated. */, + "rootDir": "src" /* Specify the root folder within your source files. */, + "moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */, + "baseUrl": ".", + "resolveJsonModule": true /* Enable importing .json files. */, + + /* JavaScript Support */ + "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, + + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "sourceMap": true /* Create source map files for emitted JavaScript files. */, + "outDir": "dist" /* Specify an output folder for all emitted files. */, + "importHelpers": true /* Allow importing helper functions from tslib once per project, instead of including them per-file. */, + + /* Interop Constraints */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + + /* Completeness */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +}