Skip to content

Commit

Permalink
Merge pull request #59 from credbull/fix-create-vault-tests
Browse files Browse the repository at this point in the history
Ensure the Create Vault integration tests are working.
  • Loading branch information
jplodge-pro authored Jun 19, 2024
2 parents b2efd6d + d66bc50 commit c78b14f
Show file tree
Hide file tree
Showing 32 changed files with 658 additions and 2,080 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ci-dev-ops.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ jobs:
ALICE_PRIVATE_KEY: ${{ secrets.DEVOPS_SEPOLIA_ALICE_PRIVATE_KEY }}
BOB_PRIVATE_KEY: ${{ secrets.DEVOPS_SEPOLIA_BOB_PRIVATE_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
SUPABASE_ANONYMOUS_KEY: ${{ vars.SUPABASE_ANONYMOUS_KEY }}
ADMIN_PASSWORD: NeverGuessThis
ALICE_PASSWORD: alice-1234
BOB_PASSWORD: bobword


- name: Cleanup Yarn Cache
if: always()
run: yarn cache clean
run: yarn cache clean
2 changes: 0 additions & 2 deletions packages/ops/.env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
SUPABASE_ANONYMOUS_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0

ADMIN_PASSWORD=NeverGuessThis
# Dev/Anvil Wallet, PrivateKey[0] (0xac097...ff80) - okay to share
ADMIN_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
Expand Down
12 changes: 8 additions & 4 deletions packages/ops/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ export default defineConfig({
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 2,
retries: 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : 1,
workers: 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [['list'], ['html', { outputFolder: 'playwright-report', open: 'never' }]],
reporter: [
['list', { printSteps: true }],
['html', { outputFolder: 'playwright-report', open: 'never' }],
],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
projects: [
{
// NOTE (JL,2024-06-18): This 'project' useful when using the Playwright Extension in vscode.
name: 'ops',
testMatch: '**/*.spec.ts', // testMatch: '**/create-vault.spec.ts',
testMatch: '**/*.spec.ts',
},
],
});
31 changes: 14 additions & 17 deletions packages/ops/src/clean-vault-table.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
import { z } from 'zod';

import { CredbullFixedYieldVault__factory } from '@credbull/contracts';

import { loadConfiguration } from './utils/config';
import { signer, supabase } from './utils/helpers';

// Zod Schema for validating parameters and configuration.
const configParser = z.object({ secret: z.object({ ADMIN_PRIVATE_KEY: z.string() }) });
import { supabaseAdminClient } from './utils/database';
import { signerFor } from './utils/ethers';
import { Schema } from './utils/schema';

/**
* Pauses all Vault contracts and truncates the Vault database table.
*
* @param config The applicable configuration object.
*
* @param config The applicable configuration object.
* @throws PostgrestError if any database operation fails.
* @throws ZodError if the `config` object does not satisfy all configuration needs.
*/
export const cleanVaultTable = async (config: any) => {
configParser.parse(config);
Schema.CONFIG_ADMIN_PRIVATE_KEY.parse(config);

const supabaseAdmin = supabase(config, { admin: true });
const supabaseAdmin = supabaseAdminClient(config);
const { data, error } = await supabaseAdmin.from('vaults').select();

if (error) throw error;

if (data.length === 0) {
console.log('No vault data to clean');
return;
}

const adminSigner = signer(config, config.secret.ADMIN_PRIVATE_KEY);
const adminSigner = signerFor(config, config.secret.ADMIN_PRIVATE_KEY);

console.log('='.repeat(80));
console.log(` Pausing ${data.length} Vaults.`)
console.log(` Pausing ${data.length} Vaults.`);
for (const vault of data) {
const contract = CredbullFixedYieldVault__factory.connect(vault.address, adminSigner);
const tx = await contract.pauseVault();
Expand All @@ -48,13 +43,15 @@ export const cleanVaultTable = async (config: any) => {
};

/**
* Invoked by the command line processor, pauses all Vault contracts and truncates the Vault database table.
*
* Invoked by the command line processor, pauses all Vault contracts and truncates the Vault database table.
*
* @throws AuthError if the account does not exist or the update fails.
* @throws ZodError if the loaded configuration does not satisfy all configuration needs.
*/
export const main = () => {
setTimeout(async () => { cleanVaultTable(loadConfiguration()) }, 1000);
setTimeout(async () => {
cleanVaultTable(loadConfiguration());
}, 1000);
};

export default main;
37 changes: 9 additions & 28 deletions packages/ops/src/create-default-users.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
import { z } from 'zod';

import { createUser } from './create-user';
import { makeAdmin } from './make-admin';
import { loadConfiguration } from './utils/config';

// Zod Schema for validating parameters and configuration.
const configSchema = z.object({
users: z.object({
admin: z.object({
email_address: z.string().email(),
}),
alice: z.object({
email_address: z.string().email(),
}),
bob: z.object({
email_address: z.string().email(),
}),
}),
secret: z.object({
ADMIN_PASSWORD: z.string(),
ALICE_PASSWORD: z.string(),
BOB_PASSWORD: z.string(),
}),
});
import { Schema } from './utils/schema';

/**
* Creates the 'default' Corporate Accounts.
*
* @param config The applicable configuration object.
* Creates the 'default' Corporate Accounts.
*
* @param config The applicable configuration object.
* @throws ZodError if the `config` object does not satisfy all configuration needs.
*/
export const createDefaultUsers = async (config: any) => {
configSchema.parse(config)
Schema.CONFIG_USERS.parse(config);

console.log('='.repeat(90));
console.log(' Creating default Users.');
Expand All @@ -47,12 +26,14 @@ export const createDefaultUsers = async (config: any) => {

/**
* Invoked by the command line processor, creates the default User Accounts.
*
*
* @throws AuthError if any account creation fails.
* @throws ZodError if the loaded configuration does not satisfy all configuration needs.
*/
export const main = () => {
setTimeout(async () => { createDefaultUsers(loadConfiguration()); }, 1000);
setTimeout(async () => {
createDefaultUsers(loadConfiguration());
}, 1000);
};

export default { main };
28 changes: 12 additions & 16 deletions packages/ops/src/create-user.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { ZodError, z } from 'zod';

import { makeChannel } from './make-channel';
import { assertEmail } from './utils/assert';
import { loadConfiguration } from './utils/config';
import { generatePassword, parseEmail, supabase } from './utils/helpers';

// Zod Schemas to validate the parameters and configuration.
const configSchema = z.object({ app: z.object({ url: z.string().url() }) });
const nonEmptyStringSchema = z.string().trim().min(1);
import { supabaseAdminClient } from './utils/database';
import { generatePassword } from './utils/generate';
import { Schema } from './utils/schema';

/**
* Creates a Corporate Account User with `email` Email Address and `_password` Password, if provided. If
* `_password` is not provided, generate a password.
* Creates a Corporate Account User with `email` Email Address and `passwordMaybe` Password, if provided. If
* `passwordMaybe` is not provided, generate a password.
*
* The `config` object is validated to provide all configuration items required.
*
* @param config The applicable configuration.
* @param email The `string` email address of the Corporate Account.
* @param isChannel The Corporate Account is also a Channel.
* @param passwordMaybe The optional password to use for the Corporate Account. If not specified, a password is generated.
* This value is injected in the returned object with the `generated_password` property name.
* @param passwordMaybe The optional password to use for the Corporate Account. If not specified, a password is
* generated. This value is injected in the returned object with the `generated_password` property name.
* @returns A `Promise` for the Supabase User object.
* @throws AuthError if the account creation fails.
* @throws ZodError if the parameters or config are invalid.
Expand All @@ -29,12 +26,11 @@ export const createUser = async (
isChannel: boolean,
passwordMaybe?: string,
): Promise<any> => {
parseEmail(email);

nonEmptyStringSchema.optional().parse(passwordMaybe);
configSchema.parse(config);
Schema.CONFIG_APP_URL.parse(config);
Schema.NON_EMPTY_STRING.optional().parse(passwordMaybe);
assertEmail(email);

const supabaseAdmin = supabase(config, { admin: true });
const supabaseAdmin = supabaseAdminClient(config);
const password = passwordMaybe || generatePassword();
const {
data: { user },
Expand Down
66 changes: 18 additions & 48 deletions packages/ops/src/create-vault.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,14 @@
import {
CredbullFixedYieldVaultFactory__factory,
CredbullFixedYieldVault__factory,
CredbullVaultFactory__factory,
} from '@credbull/contracts';
import { CredbullFixedYieldVault__factory, CredbullVaultFactory__factory } from '@credbull/contracts';
import type { ICredbull } from '@credbull/contracts/types/CredbullFixedYieldVaultFactory';
import { addYears, startOfWeek, startOfYear, subDays } from 'date-fns';
import { z } from 'zod';

import { headers, login } from './utils/api';
import { assertEmailOptional, assertUpsideVault } from './utils/assert';
import { loadConfiguration } from './utils/config';
import {
addressSchema,
headers,
login,
parseEmailOptional,
parseUsideVault,
signer,
supabase,
userByOrThrow,
} from './utils/helpers';

// Zod Schema to validate all config points in this module.

const configSchema = z.object({
secret: z.object({
ADMIN_PRIVATE_KEY: z.string(),
}),
api: z.object({
url: z.string().url(),
}),
evm: z.object({
address: z.object({
owner: addressSchema,
operator: addressSchema,
custodian: addressSchema,
treasury: addressSchema,
activity_reward: addressSchema,
}),
}),
operation: z.object({
createVault: z.object({
collateral_percentage: z.number(),
}),
}),
});
import { supabaseAdminClient } from './utils/database';
import { signerFor } from './utils/ethers';
import { Schema } from './utils/schema';
import { userByOrThrow } from './utils/user';

type CreateVaultParams = {
treasury: string | undefined;
Expand Down Expand Up @@ -133,9 +99,9 @@ function createParams(
* @param config The applicable configuration. Must be valid against a schema.
* @param isMatured `true` if the Vault is to be created matured, or not.
* @param isUpside `true` is an Fixed Yield With Upside Vault is to be created.
* @param isTenant Don't Know.
* @param isTenant (JL,2024-06-18): Don't Know.
* @param upsideVault The `string` Address of the Fixed Yield With Upside Vault.
* @param tenantEmail Don't Know.
* @param tenantEmail (JL,2024-06-18): Don't Know.
* @param override Values that, if present, override the same configuration values.
* @throws ZodError if any parameter or config item fails validation.
* @throws PostgrestError if authentication or any database interaction fails.
Expand All @@ -150,18 +116,22 @@ export const createVault = async (
tenantEmail?: string,
override?: { treasuryAddress?: string; activityRewardAddress?: string; collateralPercentage?: number },
): Promise<any> => {
configSchema.parse(config);
parseUsideVault(upsideVault);
parseEmailOptional(tenantEmail);
const supabaseAdmin = supabase(config, { admin: true });
Schema.CONFIG_API_URL.parse(config);
Schema.CONFIG_ADMIN_PRIVATE_KEY.parse(config);
Schema.CONFIG_EVM_ADDRESS.parse(config);
Schema.CONFIG_OPERATION_CREATE_VAULT.parse(config);
assertUpsideVault(upsideVault);
assertEmailOptional(tenantEmail);

const supabaseAdmin = supabaseAdminClient(config);
const addresses = await supabaseAdmin.from('contracts_addresses').select();
if (addresses.error) throw addresses.error;

console.log('='.repeat(80));

// for allowCustodian we need the Admin user. for createVault we need the Operator Key.
// the only way this works is if you go into supabase and associate the admin user with the Operator wallet
const adminSigner = signer(config, config.secret.ADMIN_PRIVATE_KEY);
const adminSigner = signerFor(config, config.secret.ADMIN_PRIVATE_KEY);

// TODO: ISSUE we are logging in here as the Admin User - but later we POST to the createVault owned by the OPERATOR
const admin = await login(config, { admin: true });
Expand Down
24 changes: 14 additions & 10 deletions packages/ops/src/deposit-with-upside.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@ import {
MockStablecoin__factory,
MockToken__factory,
} from '@credbull/contracts';
import { formatEther, parseEther, parseUnits } from 'ethers/lib/utils';
import { formatEther, parseUnits } from 'ethers/lib/utils';

import { headers, linkWalletMessage, login, signer, supabase } from './utils/helpers';
import { headers, login } from './utils/api';
import { loadConfiguration } from './utils/config';
import { supabaseAdminClient } from './utils/database';
import { linkWalletMessage, signerFor } from './utils/ethers';

export const main = () => {
setTimeout(async () => {
console.log('\n');
console.log('=====================================');
console.log('\n');
const config = loadConfiguration();

// console.log('Bob: retrieves a session through api.');
const bob = await login();
const bob = await login(config);

const bobHeaders = headers(bob);
console.log('Bob: retrieves a session through api. - OK');

// console.log('Bob: signs a message with his wallet.');
const bobSigner = signer(process.env.BOB_PRIVATE_KEY || "");
const message = await linkWalletMessage(bobSigner);
const bobSigner = signerFor(config, config.secret!.BOB_PRIVATE_KEY!);
const message = await linkWalletMessage(config, bobSigner);
const signature = await bobSigner.signMessage(message);
console.log('Bob: signs a message with his wallet. - OK');

// console.log('Bob: sends the signed message to Credbull so that he can be KYC`ed.');
await fetch(`${process.env.API_BASE_URL}/accounts/link-wallet`, {
await fetch(`${config.api.url}/accounts/link-wallet`, {
method: 'POST',
body: JSON.stringify({ message, signature, discriminator: 'bob@partner.com' }),
body: JSON.stringify({ message, signature, discriminator: config.users.bob.email_address }),
...bobHeaders,
});
console.log('Bob: sends the signed message to Credbull so that he can be KYC`ed. - OK');
Expand All @@ -37,15 +41,15 @@ export const main = () => {
const admin = await login({ admin: true });
const adminHeaders = headers(admin);

await fetch(`${process.env.API_BASE_URL}/accounts/whitelist`, {
await fetch(`${config.api.url}/accounts/whitelist`, {
method: 'POST',
body: JSON.stringify({ user_id: bob.user_id, address: bobSigner.address }),
...adminHeaders,
});
console.log('Admin: receives the approval and KYCs Bob. - OK');

// console.log('Bob: queries for existing vaults.');
const vaultsResponse = await fetch(`${process.env.API_BASE_URL}/vaults/current`, {
const vaultsResponse = await fetch(`${config.api.url}/vaults/current`, {
method: 'GET',
...bobHeaders,
});
Expand All @@ -66,7 +70,7 @@ export const main = () => {
await approveTx.wait();
console.log('Bob: gives the approval to the vault to swap it`s USDC. - OK');

const client = supabase({ admin: true });
const client = supabaseAdminClient(config);
const addresses = await client.from('contracts_addresses').select();
if (addresses.error) return addresses;

Expand Down
Loading

0 comments on commit c78b14f

Please sign in to comment.