Skip to content

Commit

Permalink
Merge pull request #18 from CirclesUBI/feat/streamline-for-v1-usage
Browse files Browse the repository at this point in the history
Feat/streamline for v1 usage
  • Loading branch information
jaensen authored May 28, 2024
2 parents 58b0712 + 6006d00 commit 64247f6
Show file tree
Hide file tree
Showing 122 changed files with 8,371 additions and 6,872 deletions.
17 changes: 2 additions & 15 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,14 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
ref: 20240215-examples

- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '21'

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Build Contracts & SDK
run: |
echo "Building contracts and SDK"
./buildContracts.sh
npm i
npm run build
- name: Build example app
- name: Build SDK
run: |
cd examples/svelte-playground
echo "Building SDK"
npm i
cp .env.example .env
npm run build
8 changes: 0 additions & 8 deletions .gitmodules

This file was deleted.

264 changes: 61 additions & 203 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,246 +1,104 @@
# Circles SDK
The Circles SDK is a library that allows you to interact with the Circles protocol.
It supports version 1 and 2 of the Circles contracts.

*Warning: This library is in a very early development stage. Things are broken and the api surface is pretty much WIP.*
The Circles SDK is a TypeScript library designed to simplify the interaction with
the [circles-contracts](https://github.com/circlesUBI/circles-contracts).

## Getting started
This section will guide you through the process of setting up a local development environment for Circles.
## Installation

### Prerequisites
Make sure you have all the following prerequisites installed:
* [git](https://git-scm.com/)
* [jq](https://jqlang.github.io/jq/)
* [nodejs](https://nodejs.org/)
* [foundry](https://getfoundry.sh/)
Install the Circles SDK using npm:

### 1) Clone the repository:
This is a monorepo using npm workspaces.
```bash
git clone --branch 20240215-examples https://github.com/CirclesUBI/circles-sdk.git
cd circles-sdk
npm install @circles-sdk/sdk
```

### 2) Build the SDK
```bash
./buildContracts.sh
npm i
npm run build
```

### 3) Run anvil
```bash
anvil --port 8545 --gas-limit 8000000 --accounts 10 --code-size-limit 32000
```
When [anvil](https://book.getfoundry.sh/reference/anvil/) was started with default values, you can use the following values to connect to:
* URL: http://localhost:8545
* Address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
* Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

If you need more accounts, see anvil's console output.

### 4) Deploy the contracts
The SDK comes with a script that deploys all relevant contracts with `forge create`.
The `deployContracts.sh` script can be configured with environment variables or an `.env` file.
## Initialization

Create custom .env files if you want to deploy to other targets. For a local deployment, you can use `.env.anvil`:
```bash
./deployContracts.sh .env.anvil
```
Configure the Circles SDK with a Circles RPC and a Pathfinder endpoint.

After the deployment, the script will print the addresses of the deployed contracts.
```
Summary:
========
V1 Hub: 0x5FbDB2315678afecb367f032d93F642f64180aa3
V2 Hub: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
```
```typescript
import { ChainConfig } from "@circles-sdk/sdk";

### 5) Run the example application
```bash
cd examples/svelte-playground
npm install
cp .env.example .env
npm run dev
const chainConfig: ChainConfig = {
circlesRpcUrl: 'https://rpc.aboutcircles.com',
pathfinderUrl: 'https://pathfinder.aboutcircles.com'
};
```

### 6) Run tests
To run the 'jest' tests, use the following command in the repository root directory:
```bash
npm run test
```
Additionally, you need an ethers.js provider and a signer. Assuming you are using MetaMask:

### 7) Configure the Circles SDK
You can use the following code to configure the Circles SDK so that it uses the local anvil environment.
Here we're using the values from above:
```typescript
import {Sdk} from '@circles-sdk/sdk/dist';
import {ethers} from "ethers";
import { ethers } from "ethers";

const privateKey = '0x..';

const rpcUrl = 'http://localhost:8545';
const v1HubAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3';
const v2HubAddress = '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512';

const jsonRpcProvider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(privateKey, jsonRpcProvider);
const sdk = new Sdk(v1HubAddress, v2HubAddress, wallet);
const windowEthereum = (window as any).ethereum;
if (!windowEthereum) {
throw new Error('window.ethereum is not installed');
}
const browserProvider = new ethers.BrowserProvider(windowEthereum);
const signer = await browserProvider.getSigner();
```

### Next steps
* Explore the methods of the sdk and avatar object.
* You can create an avatar for any account to access its public data.

## Reference
### Sdk configuration
In order to use the sdk you must supply the contracts addresses and a provider:
Initialize and use the Circles SDK:

```typescript
import { Sdk } from '@circles-sdk/sdk';

const v1HubAddress = '0x123...';
const v2HubAddress = '0x123...';
const wallet = // ethers.Wallet or ethers.providers.JsonRpcProvider

const sdk = new Sdk(v1HubAddress, v2HubAddress, wallet);
const sdk = new Sdk(chainConfig, signer);
```

### Avatar
#### Create an avatar
To interact with Circles, you need an avatar. You can create one by calling `createAvatar`.
The address can be any address (EOA, smart contract wallet) that you control.

```typescript
const avatar = await sdk.createAvatar("0x123..."); // Supply the avatar address
await avatar.init();
```
Depending on your previous usage of the address, the avatar will be initialized in one of these states:
```typescript
enum AvatarState {
Unregistered, // The address has not been used with Circles before
V1_Human, // The address is only a V1 human
V1_StoppedHuman, // The address is only a V1 human that has been stopped
V1_Organization, // The address is only a V1 organization
V2_Human, // The address is only a V2 human
V2_Group, // The address is only a V2 group
V2_Organization, // The address is only a V2 organizations
V1_StoppedHuman_and_V2_Human, // The address is a V1 human that has been stopped and a V2 human
Unknown // The address has been used with Circles before, but the state is unknown
}
```
## Usage

#### Use an existing Circles account
If you already have a v1 Circles account, you can upgrade it to v2.

After upgrading,
* ... you will have a new personal v2 token that you can use to mint.
* ... you can convert all your v1 token holdings to v2 tokens as long as they're available on v2 already.

If your friends haven't upgraded to v2 yet, you can 'invite' them. This will cost you a fee, but everyone will be able to convert v1 tokens of the invited person to v2 tokens immediately (See *'Invite a friend'* below).
### Avatar

Note: *If these prerequisites aren't met, you must be invited by an existing member instead.*
For regular Circles interactions, use the Avatar class:

##### 1) Prerequisites:
* The registration period is not over
* Your v1 token must be stopped
```typescript
// Check if the registration period is already over
if (await sdk.isRegistrationPeriodOver()) {
throw new Error('The registration period is already over');
}
const avatar = await sdk.getAvatar("0x1234...");
````

// Check if the avatar is a V1 human and has been stopped
if (avatar.state.value !== AvatarState.V1_StoppedHuman) {
throw new Error('You cannot register at Circles v2 because your v1 token is not stopped');
}
```
If you're v1 token is not stopped, you can stop it like this:
```typescript
if (avatar.state.value !== AvatarState.V1_Human) {
throw new Error(`You don't have a v1 token`);
}
const txReceipt = await avatar.stopV1();
```
The `getAvatar` method will throw an error if the address is not registered.
Use `sdk.data.getAvatarInfo` to check if an address is registered.

##### 2) Register at Circles v2:
In Circles v2, every human has a profile. The profile is a JSON object that is stored on IPFS.
The profile is identified by a CIDv0 (Content Identifier). The CID is updatable for the case that the profile changes in the future.
If you want to sign the connected wallet up for Circles, use the `registerHuman`
or `registerOrganization` methods.

The profile schema is defined in [ERC-1155 Metadata URI JSON Schema](https://eips.ethereum.org/EIPS/eip-1155#erc-1155-metadata-uri-json-schema).
```typescript
const cidV0 = 'Qm...'; // CIDv0 of your profile
const txReceipt = await avatar.registerHuman(cidV0);
```
##### 3) Verify:
If the registration was successful, the state of your avatar should have changed to `V1_StoppedHuman_and_V2_Human`.
```typescript
if (avatar.state.value !== AvatarState.V1_StoppedHuman_and_V2_Human) {
throw new Error('Something went wrong');
}
const human = await sdk.registerHuman();
const organization = await sdk.registerOrganization();
```

#### Invite a friend
If you are already a member of Circles, you can invite a friend to join.
After the end of the registration period, this is the only way for new people to register at Circles.

The friend is identified by their address. The address can be any address (EOA, smart contract wallet) that your friend controls.
Note that a wallet can only be signed up for Circles once. Either as a human or as an organization.

You can invite someone in order to:
* ... allow them to join Circles for the first time
* ... allow them to upgrade their existing accounts
* ... convert v1 token holdings to v2 tokens if the person hasn't upgraded yet
The `Avatar` class provides the following methods:

After inviting,
* ... the invited person will have a new personal v2 token.
* ... you (and others) can convert v1 token holdings of the invitee's token to v2 tokens.
* __trust__: Trusts another avatar. Trusting an avatar means you're willing to accept Circles that
have been issued by this avatar.
* __untrust__: Revokes trust from another avatar. This means you will no longer accept Circles
issued by this avatar.
* __getMintableAmount__: Gets the amount available to mint via `personalMint`.
* __personalMint__: Mints the available Circles for the avatar.
* __transfer__: Transfers Circles to another avatar.
* __getTrustRelations__: Gets the current incoming and outgoing trust relations of the avatar.
* __getTotalBalance__: Gets the total balance of the avatar.
* __getTransactionHistory__: Gets the transaction history of the avatar.

Note: *The inviter must pay an invitation fee. The invitee will get a welcome bonus.*
### Data

##### 1) Prerequisites:
```typescript
// Mint the outstanding amount of personal tokens to maximize the chances of being able to invite someone.
await avatar.mintPersonalTokens();

// How high is the invitation fee?
const invitationFee: bigint = await sdk.getInvitationFee();
If you are only interested in reading Circles data, use the CirclesData class:

// Whats the balance of the avatar's own token?
const ownTokenBalance: bigint = await avatar.getTokenBalance();

// Can be paid?
if (invitationFee > ownTokenBalance) {
throw new Error('You must mint more personal tokens before you can invite someone.');
}
```

##### 2) Send the invitation:
```typescript
const txReceipt = await avatar.inviteHuman('0x123...');
```
import { CirclesData, CirclesRpc } from '@circles-sdk/data';

##### 3) Verify:
If the invitation was successful, the state of the invited avatar should have changed to `V2_Human`.
```typescript
const invitedAvatar = await sdk.createAvatar('0x123...');
await invitedAvatar.init();

if (invitedAvatar.state.value !== AvatarState.V2_Human) {
throw new Error('Something went wrong');
}
const rpc = new CirclesRpc(chainConfig.circlesRpcUrl);
const data = new CirclesData(rpc);
```

#### Update your profile
In Circles v2, every human has a profile. The profile is a JSON object that is stored on IPFS.
The profile is identified by a CIDv0 (Content Identifier). The CID is updatable for the case that the profile changes in the future.
The `CirclesData` class provides the following methods:

The profile schema is defined in [ERC-1155 Metadata URI JSON Schema](https://eips.ethereum.org/EIPS/eip-1155#erc-1155-metadata-uri-json-schema).
* __getAvatarInfo__: Gets basic information about an avatar, including signup timestamp, Circles
version, avatar type, and token address/id.
* __getTotalBalance__: Gets the total balance of an avatar.
* __getTokenBalances__: Gets the detailed token balances of an avatar.
* __getTransactionHistory__: Gets the transaction history of an avatar.
* __getTrustRelations__: Gets the current incoming and outgoing trust relations of an address.

Use this method, if
* ... you want to update your profile.
* ... you have been invited and don't have a profile yet.

```typescript
const cidV0 = 'Qm...'; // New CIDv0 of your profile
const txReceipt = await avatar.updateProfile(cidV0);
```
If you need more control about the queried data, you can query the RPC directly. Please refer to the
[circles-nethermind-plugin](https://github.com/CirclesUBI/circles-nethermind-plugin?tab=readme-ov-file#quickstart)
docs for more information.
11 changes: 0 additions & 11 deletions examples/svelte-playground/.env.example

This file was deleted.

13 changes: 0 additions & 13 deletions examples/svelte-playground/.eslintignore

This file was deleted.

Loading

0 comments on commit 64247f6

Please sign in to comment.