Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access control via Stellar smart contract example #10

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions lit-access-control-conditions-stellar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Running this Code Example

## Prerequisites

You'll need to have the contract address for a deployed instance of the [allow-list contract](./stellar-contracts/contracts/allow-list/src/lib.rs). Additionally, the `init` function on the contract should be called with the Stellar account you'd like to use as the admin of the contract (being an admin allows adding new address to the allow list).

### Deploying the Contract to Stellar Testnet

1. Follow [this setup guide](https://developers.stellar.org/docs/smart-contracts/getting-started/setup#install-the-target) to setup the `soroban` CLI
2. Configure an identity to submit transaction to the testnet:
```
soroban keys generate --global alice --network testnet
```
3. Compile the smart contract:
```
soroban contract build
```
4. Deploy the contract
```
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/allow_list.wasm \
--source alice \
--network testnet
```
The output of this command will be the smart contract address we use to submit transactions to, make sure to copy it and save it for later (you're going to need to paste it in our [litAction.js](./nodejs/src/litAction.js) for the `ALLOW_LIST_CONTRACT_ADDRESS` `const`):
```
CBNUWSEPUI6DTKT7IYANIOYVFPWNVGELAUJY4HE4NQSEWW3I25BKWP6M
```

### Initializing the Contract

You should call the `init` function the contract to set the admin for the contract:

```
soroban contract invoke \
--id CBNUWSEPUI6DTKT7IYANIOYVFPWNVGELAUJY4HE4NQSEWW3I25BKWP6M \
--source alice \
--network testnet \
-- \
init \
--admin GCPQNAWI7DZ2OXVFP5ZWD7224HOOJVL6WRIMMEJ6PGS3ABMHFWC6ER6I
```

The address passed as the `--admin` parameter will be the only addressed allow to add other address to the allow list.

### Verifying `admin` was Added to the Allow List

You can call the `is_allowed` function to verify the admin address we just initialized the contract with is in fact on the allow list:

```
soroban contract invoke \
--id CBNUWSEPUI6DTKT7IYANIOYVFPWNVGELAUJY4HE4NQSEWW3I25BKWP6M \
--source alice \
--network testnet \
-- \
id_allowed \
--address GCPQNAWI7DZ2OXVFP5ZWD7224HOOJVL6WRIMMEJ6PGS3ABMHFWC6ER6I
```

## Running the Code

Now that we have a Stellar smart contract deployed and initialized, follow these steps to run the code example:

1. `cd` into the `nodejs` directory
2. Install the project dependencies with `yarn`
3. `cp .env.example .env` and fill in the required ENVs:
```
STELLAR_SECRET=
STELLAR_ACCOUNT_SEQUENCE_NUMBER=0
ETHEREUM_PRIVATE_KEY=
LIT_ACTION_IPFS_CID=
LIT_PKP_PUBLIC_KEY=
```
4. In order to set the `LIT_ACTION_IPFS_CID` ENV, we'll need to build our Lit Action code and upload it to IPFS. To build it, run `yarn build:lit-action`. Then you can upload the resulting file found under `dist/litAction.js` to IPFS and get the CID
- Before doing this, remember to replace the value for `ALLOW_LIST_CONTRACT_ADDRESS` in [litAction.js](./nodejs/src/litAction.js) to the address of the contract you deployed and initialized with your admin address
5. Finally, run `yarn start` to execute the code found in [index.js](./nodejs/src/index.ts)
5 changes: 5 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
STELLAR_SECRET=
STELLAR_ACCOUNT_SEQUENCE_NUMBER=0
ETHEREUM_PRIVATE_KEY=
LIT_ACTION_IPFS_CID=
LIT_PKP_PUBLIC_KEY=
4 changes: 4 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.env
node_modules
dist
.DS_Store
6 changes: 6 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/deno.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"tasks": {
"bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run esbuild.js",
"test": "deno run --allow-net dist/litAction.js"
}
}
63 changes: 63 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/esbuild-shims.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
globalThis.process = {
env: {},
};
15 changes: 15 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as esbuild from "https://deno.land/x/[email protected]/mod.js";
import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@^0.10.3";

esbuild.build({
plugins: [...denoPlugins()],
entryPoints: ["src/litAction.js"],
outdir: "dist/",
bundle: true,
platform: "browser",
format: "esm",
target: "esnext",
minify: false,
inject: ["./esbuild-shims.js"],
});
await esbuild.stop();
27 changes: 27 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "lit-access-control-conditions-stellar-in-nodejs",
"version": "0.1.0",
"description": "Example of Lit Action Access Control Conditions using Stellar in Node.js",
"license": "MIT",
"type": "module",
"scripts": {
"build:lit-action": "deno task bundle",
"start": "npx @dotenvx/dotenvx run -- node --loader ts-node/esm src/index.ts",
"test:lit-action": "deno task test"
},
"dependencies": {
"@dotenvx/dotenvx": "^0.37.1",
"@lit-protocol/auth-helpers": "^6.0.0-beta.1",
"@lit-protocol/constants": "^6.0.0-beta.1",
"@lit-protocol/contracts-sdk": "^6.0.0-beta.1",
"@lit-protocol/lit-node-client-nodejs": "^6.0.0-beta.1",
"@stellar/stellar-base": "^11.1.0",
"ethers": "5.7.2"
},
"devDependencies": {
"ts-node": "^10.9.2",
"tsc": "^2.0.4",
"tslib": "^2.6.2",
"typescript": "^5.4.5"
}
}
110 changes: 110 additions & 0 deletions lit-access-control-conditions-stellar/nodejs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import * as Ethers from "ethers";
import { LitNodeClientNodeJs } from "@lit-protocol/lit-node-client-nodejs";
import { LitNetwork } from "@lit-protocol/constants";
import {
LitAbility,
LitActionResource,
LitPKPResource,
createSiweMessageWithRecaps,
generateAuthSig,
} from "@lit-protocol/auth-helpers";
import * as StellarBase from "@stellar/stellar-base";

const getEnv = (name: string): string => {
const env = process.env[name];
if (env === undefined || env === "")
throw new Error(
`${name} ENV is not defined, please define it in the .env file`
);
return env;
};

const STELLAR_SECRET = getEnv("STELLAR_SECRET");
const STELLAR_ACCOUNT_SEQUENCE_NUMBER = getEnv(
"STELLAR_ACCOUNT_SEQUENCE_NUMBER"
);
const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY");
const LIT_ACTION_IPFS_CID = getEnv("LIT_ACTION_IPFS_CID");
const LIT_PKP_PUBLIC_KEY = getEnv("LIT_PKP_PUBLIC_KEY");

let litNodeClient: LitNodeClientNodeJs | undefined = undefined;

try {
const stellarKeyPair = StellarBase.Keypair.fromSecret(STELLAR_SECRET);
const stellarAccount = new StellarBase.Account(
stellarKeyPair.publicKey(),
STELLAR_ACCOUNT_SEQUENCE_NUMBER
);

const stellarAuthTx = new StellarBase.TransactionBuilder(stellarAccount, {
fee: StellarBase.BASE_FEE,
networkPassphrase: StellarBase.Networks.TESTNET,
})
.setTimeout(60 * 60 * 24) // 24 hours
.build();
stellarAuthTx.sign(stellarKeyPair);

litNodeClient = new LitNodeClientNodeJs({
litNetwork: LitNetwork.Cayenne,
});
await litNodeClient.connect();

const ethersWallet = new Ethers.Wallet(
ETHEREUM_PRIVATE_KEY,
new Ethers.providers.JsonRpcProvider(
"https://chain-rpc.litprotocol.com/http"
)
);
const sessionSigs = await litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours
resourceAbilityRequests: [
{
resource: new LitPKPResource("*"),
ability: LitAbility.PKPSigning,
},
{
resource: new LitActionResource("*"),
ability: LitAbility.LitActionExecution,
},
],
authNeededCallback: async ({
resourceAbilityRequests,
expiration,
uri,
}) => {
const toSign = await createSiweMessageWithRecaps({
// @ts-ignore
uri,
// @ts-ignore
expiration,
// @ts-ignore
resources: resourceAbilityRequests,
walletAddress: await ethersWallet.getAddress(),
nonce: await litNodeClient!.getLatestBlockhash(),
litNodeClient,
});
return await generateAuthSig({
signer: ethersWallet,
toSign,
});
},
});

const litPkpSignature = await litNodeClient.executeJs({
sessionSigs,
ipfsId: LIT_ACTION_IPFS_CID,
jsParams: {
stellarPublicKey: stellarKeyPair.publicKey(),
stellarAuthTxHash: stellarAuthTx.hash(),
stellarAuthTxSignature: stellarAuthTx.signatures[0].signature(),
stellarAccountSequenceNumber: STELLAR_ACCOUNT_SEQUENCE_NUMBER,
litPkpPublicKey: LIT_PKP_PUBLIC_KEY,
},
});
console.log("litPkpSignature: ", litPkpSignature);
} catch (error) {
console.error(error);
} finally {
litNodeClient!.disconnect();
}
Loading