Skip to content

feat: tee solver registry, vault and server#1

Merged
think-in-universe merged 42 commits intomainfrom
feat/tee-solver
Jun 15, 2025
Merged

feat: tee solver registry, vault and server#1
think-in-universe merged 42 commits intomainfrom
feat/tee-solver

Conversation

@think-in-universe
Copy link
Copy Markdown
Collaborator

@think-in-universe think-in-universe commented Jun 2, 2025

NEAR Intents TEE Solver Registry

The NEAR Intents TEE Solver Registry is a protocol that enables secure and private execution of NEAR Intents solvers using Trusted Execution Environment (TEE) technology. This project consists of smart contracts for managing solver registration and a server for launching and managing TEE solvers.

This protocol allows liquidity pools creation for NEAR Intents. Liquidity providers can transfer funds into the pools' smart contracts. Only the solvers who're running within TEE with the approved Docker images can be registered and authorized to operate against the pools' assets.

Components

Smart Contracts

  • solver-registry: Support liquidity pools creation. Manage registration and verification of TEE solvers for each liquidity pool.
  • intents-vault: The vault contract that manage the pool's asset within NEAR Intents.

Solver Management Server

  • Monitoring liquidity pools and CVMs
  • Start TEE solvers for a newly created liquidity pool

TEE enabled AMM Solver

  1. Intents AMM Solver within TEE (docker image)

TODOs

  1. test solver registry and intents vault contracts in both sandbox and mainnet
  2. contract enhancements
    • verify derived public key
    • the public key format for calling add_public_key function
  3. update Intents AMM Solver within TEE and TEE solver server
  4. run end to end test on mainnet
  5. documentation
  6. enhancements
    • charge appropriate fees to cover the cost of liquidity pool
    • deploy CVMs only if there're enough funds in the pool
    • more operational and security enhancements for production
    • review and verify environment variables
    • use the pool fee from smart contract
    • enhance view function's security by comparing results of multiple RPC servers
    • enhance intents public key's permission control
    • adding and removing liquidity: not priority now

@think-in-universe think-in-universe changed the title feat: solver registry and intents vault [WIP] feat: solver registry and intents vault Jun 2, 2025
@think-in-universe think-in-universe changed the title [WIP] feat: solver registry and intents vault [WIP] feat: tee solver registry, vault and server Jun 2, 2025
Comment on lines +26 to +35
AddLiquidity {
pool_id: &'a AccountId,
account_id: &'a AccountId,
amounts: &'a Vec<U128>,
},
RemoveLiquidity {
pool_id: &'a AccountId,
account_id: &'a AccountId,
amounts: &'a Vec<U128>,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these two are needed. The flow is basically that once a token graduates from the launchpad, it calls create_liquidity_pool with the tokens left in the launchpad

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. As discussed, this will be considered for next milestones. We'll skip adding/removing liquidity features in this PR.


log!("verify result: {:?}", result);

// TODO: verify predecessor implicit account is derived from this public key
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you also need to check that this key is generated from within the TEE. This should be part of the quote verification. The RTMR3 hash should be extended with the public key, i.e, rtmr3 = hash(rmtr3 || public_key)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been implemented by adding the derived public key as the raw report data when generating the quote hex.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this comment then

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO is to verify the predecessor account's address (implicit account) matches with the public key. This can be optional.

Let me confirm and may add this enhancement later.

@think-in-universe think-in-universe changed the title [WIP] feat: tee solver registry, vault and server feat: tee solver registry, vault and server Jun 9, 2025
@think-in-universe
Copy link
Copy Markdown
Collaborator Author

think-in-universe commented Jun 10, 2025

@bowenwang1996 Hey Bowen, this PR is not perfect and has some limitations that we need to enhance as I mentioned in the PR's description, but it's ready for review. Please invite any reviewer who needs to look into this system.


export const ENV = requiredEnv('ENV') as Env;

export function requiredEnv(name: string): string {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use Zod and simplify this and index.ts by quite a bit. No need for custom env management/validation

@@ -0,0 +1,60 @@
import { getConfig } from '../config';
import { connect, KeyPair, keyStores, Near } from 'near-api-js';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use near-api-js, that's actually deprecated. Use @near-js/account, @near-js/client, or @near-js/providers. It includes all the functions you've written here.

In fact, you might not even need this utils file at all

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. We can refactor with either near-api-js v6.0.0+ or @near-js/* packages later in another PR.

I'll keep the utils in this PR.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

near-api-js as a package is deprecated. v6 recommended moving to @near-js. It's all part of the same monorepo, which can be a bit confusing

https://github.com/near/near-api-js

Comment on lines +1 to +3
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { getApiKey } from '../utils/credentials';
import { logger } from '../../utils/logger';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why vendor this library? You can npm install it and import the APIClient instead.

It'll be a lot to manually maintain this and keep it synced with https://github.com/Phala-Network/phala-cloud-cli

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally yes, but I think ApiClient is not exported in the phala package.

How do you think we can import ApiClient?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do export it here: https://github.com/Phala-Network/phala-cloud-cli/blob/main/src/api/client.ts#L18

However, I see that the package.json bundles the code, and installing from Git doesn't really help either, since it's expecting built-files only. Another option would be to fork it to support importing it as a library.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just opened an issue in the repo and a corresponding PR to fix that. If/once they merge, then we no longer need to vendor this library 😄

}

public async getWorkerLen(): Promise<number> {
await this.init();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for multiple inits - you can move this to a constructor, or if you'd prefer, just make everything static methods and keep the inits.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was supposed to use constructor in the initial implementation, but constructor function cannot be async. We can refactor this later.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it looks like:
init() calls getConfig() which is async
getConfig() dynamically imports either mainnet.ts or testnet.ts based on the ENV variable
But ENV is determined at module load time from process.env.ENV

  constructor() {
    // This could be done synchronously since ENV is available immediately
    const config = ENV === 'mainnet' ? mainnetConfig : testnetConfig;
    this.solverRegistryContract = config.near.contract.solverRegistry;
  }

Also dynamically importing modules is not really ideal.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. make sense. will update this later.

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for commonjs support, that's a legacy setting

"start": "node -r dotenv/config dist/index.js",
"dev": "ts-node -r dotenv/config src/index.ts",
"test": "jest",
"lint": "eslint . --ext .ts"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out biome for linting/formatting instead of eslint

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. we may migrate to biome later.

#[near(contract_state)]
pub struct Contract {}

/// TODO: the contract can be deployed as a global contract
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create an issue for this one

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue has been created: #3


log!("verify result: {:?}", result);

// TODO: verify predecessor implicit account is derived from this public key
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this comment then

Comment on lines +159 to +162
// fn require_approved_codehash(&self) {
// let worker = self.get_worker(env::predecessor_account_id());
// self.assert_approved_codehash(&worker.codehash);
// }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this commented out?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion function is used when the worker invokes some functions via the registry contract (e.g. get Chain Signatures of some intents via a sign_intents function), but in practice we didn't use this function yet. I prefer keep the commented code for now, and we can remove it later if there's no such use case.

Comment on lines +136 to +161
// `add_liquidity` and `remove_liquidity` are not needed for now
// #[payable]
// pub fn add_liquidity(
// &mut self,
// pool_id: u32,
// token_ids: Vec<AccountId>,
// amounts: Vec<Balance>,
// ) {
// require!(token_ids.len() == 2, "Must have exactly 2 tokens");
// require!(amounts.len() == 2, "Must have exactly 2 amounts");
// require!(amounts[0] > 0, "Amount must be greater than 0");
// require!(amounts[1] > 0, "Amount must be greater than 0");

// let pool = self.pools.get(pool_id).expect("Pool not found");
// let shares_total_supply = pool.shares_total_supply;
// }

// #[payable]
// pub fn remove_liquidity(&mut self, pool_id: u32, shares: U128) {
// let shares = shares.0;
// require!(shares > 0, "Shares must be greater than 0");

// let pool = self.pools.get(pool_id).expect("Pool not found");
// // pool.shares_total_supply -= shares;
// }
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create an issue for allowing users to add liquidity

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I have created an issue: #2

@think-in-universe think-in-universe merged commit 4543305 into main Jun 15, 2025
5 checks passed
@think-in-universe think-in-universe deleted the feat/tee-solver branch June 15, 2025 17:02
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"@typescript-eslint/eslint-plugin": "^5.57.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is several major versions behind. The latest is 8.34.0

"@types/node": "^18.15.11",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"eslint": "^8.37.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a major version behind, the latest is 9.29.0

},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node 18 is being deprecated in 1 month

"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"tsx": "^4.20.3",
"typescript": "^5.0.3"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version of typescript is over a year old

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll update the dependencies this week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants