Skip to content

Commit

Permalink
Merge 5453256 into f9e5ca3
Browse files Browse the repository at this point in the history
  • Loading branch information
alfetopito committed Jun 7, 2022
2 parents f9e5ca3 + 5453256 commit b02ea4a
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 16 deletions.
19 changes: 15 additions & 4 deletions src/api/metadata/metadata.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock'
import { CowSdk } from '../../CowSdk'
import { CowError } from '../../utils/common'
import { DEFAULT_IPFS_READ_URI, DEFAULT_IPFS_WRITE_URI } from '../../constants'

enableFetchMocks()

Expand All @@ -18,7 +19,8 @@ const DEFAULT_APP_DATA_DOC = {
}

const IPFS_HASH = 'QmWtoqEQKSUvwUqnCEyoLeJ5SfnCzPhWerVDXjBBjjnj9t'

const PINATA_API_KEY = 'apikey'
const PINATA_API_SECRET = 'apiSecret'
const APP_DATA_HEX = '0x7f1a65839d8801753d270a067c6cfaface303af6506531f3362a3001f25bb153'

const CUSTOM_APP_DATA_DOC = {
Expand Down Expand Up @@ -65,18 +67,27 @@ test('Invalid: Upload to IPFS without passing credentials', async () => {
test('Valid: Upload AppDataDoc to IPFS', async () => {
fetchMock.mockResponseOnce(JSON.stringify({ IpfsHash: IPFS_HASH }), { status: HTTP_STATUS_OK })
const appDataDoc = cowSdk.metadataApi.generateAppDataDoc(CUSTOM_APP_DATA_DOC.metadata)
const cowSdk1 = new CowSdk(chainId, { ipfs: { pinataApiKey: 'validApiKey', pinataApiSecret: 'ValidApiSecret' } })
const cowSdk1 = new CowSdk(chainId, { ipfs: { pinataApiKey: PINATA_API_KEY, pinataApiSecret: PINATA_API_SECRET } })
const appDataHex = await cowSdk1.metadataApi.uploadMetadataDocToIpfs(appDataDoc)
expect(fetchMock).toHaveBeenCalledTimes(1)
expect(appDataHex).toEqual(APP_DATA_HEX)
expect(fetchMock).toHaveBeenCalledWith(DEFAULT_IPFS_WRITE_URI + '/pinning/pinJSONToIPFS', {
body: JSON.stringify({ pinataContent: appDataDoc, pinataMetadata: { name: 'appData' } }),
headers: {
'Content-Type': 'application/json',
pinata_api_key: PINATA_API_KEY,
pinata_secret_api_key: PINATA_API_SECRET,
},
method: 'POST',
})
})

test('Invalid: Upload AppDataDoc to IPFS with wrong credentials', async () => {
fetchMock.mockResponseOnce(JSON.stringify({ error: { details: 'IPFS api keys are invalid' } }), {
status: HTTP_STATUS_INTERNAL_ERROR,
})
const appDataDoc = cowSdk.metadataApi.generateAppDataDoc({})
const cowSdk1 = new CowSdk(chainId, { ipfs: { pinataApiKey: 'InvalidApiKey', pinataApiSecret: 'InvValidApiSecret' } })
const cowSdk1 = new CowSdk(chainId, { ipfs: { pinataApiKey: PINATA_API_KEY, pinataApiSecret: PINATA_API_SECRET } })
try {
await cowSdk1.metadataApi.uploadMetadataDocToIpfs(appDataDoc)
await expect(cowSdk1.metadataApi.uploadMetadataDocToIpfs(appDataDoc)).rejects.toThrow('IPFS api keys are invalid')
Expand All @@ -92,7 +103,7 @@ test('Valid: Decode appData ', async () => {
const appDataDoc = await cowSdk.metadataApi.decodeAppData(APP_DATA_HEX)

expect(fetchMock).toHaveBeenCalledTimes(1)
expect(fetchMock).toHaveBeenCalledWith(`https://gnosis.mypinata.cloud/ipfs/${IPFS_HASH}`)
expect(fetchMock).toHaveBeenCalledWith(`${DEFAULT_IPFS_READ_URI}/${IPFS_HASH}`)
expect(appDataDoc?.version).toEqual(CUSTOM_APP_DATA_DOC.version)
expect(appDataDoc?.appCode).toEqual(CUSTOM_APP_DATA_DOC.appCode)
expect(appDataDoc?.metadata.referrer?.address).toEqual(CUSTOM_APP_DATA_DOC.metadata.referrer.address)
Expand Down
3 changes: 2 additions & 1 deletion src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'

export const DEFAULT_APP_DATA_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000'

export const DEFAULT_IPFS_GATEWAY_URI = 'https://gnosis.mypinata.cloud/ipfs'
export const DEFAULT_IPFS_READ_URI = 'https://gnosis.mypinata.cloud/ipfs'
export const DEFAULT_IPFS_WRITE_URI = 'https://api.pinata.cloud'
5 changes: 4 additions & 1 deletion src/schemas/appData.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
},
"quote": {
"$id": "#/definitions/quote",
"required": ["sellAmount", "buyAmount"],
"required": ["sellAmount", "buyAmount", "version"],
"title": "Quote",
"type": "object",
"properties": {
Expand All @@ -94,6 +94,9 @@
"buyAmount": {
"$ref": "#/definitions/bigNumber",
"title": "Quote buy amount"
},
"version": {
"$ref": "#/definitions/version"
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/utils/appData.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,17 @@ test('Valid IPFS appData from CID', async () => {
})

test('Valid: quote metadata - minimal', async () => {
const document = { ...BASE_DOCUMENT, metadata: { quote: { sellAmount: '1', buyAmount: '1' } } }
const document = { ...BASE_DOCUMENT, metadata: { quote: { sellAmount: '1', buyAmount: '1', version: '0.1.0' } } }
const validation = await validateAppDataDocument(document)

expect(validation).toEqual(VALID_RESULT)
})

test('Valid: quote metadata - with all fields', async () => {
const document = { ...BASE_DOCUMENT, metadata: { quote: { sellAmount: '1', buyAmount: '1', id: 'S09D8ZFAX' } } }
const document = {
...BASE_DOCUMENT,
metadata: { quote: { sellAmount: '1', buyAmount: '1', id: 'S09D8ZFAX', version: '0.1.0' } },
}
const validation = await validateAppDataDocument(document)

expect(validation).toEqual(VALID_RESULT)
Expand Down
4 changes: 2 additions & 2 deletions src/utils/appData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Ajv, { ValidateFunction } from 'ajv'
import { fromHexString } from './common'
import { DEFAULT_IPFS_GATEWAY_URI } from '../constants'
import { DEFAULT_IPFS_READ_URI } from '../constants'
import { AppDataDoc } from '../types'

let validate: ValidateFunction | undefined
Expand Down Expand Up @@ -40,7 +40,7 @@ export async function getSerializedCID(hash: string): Promise<void | string> {
return CID.decode(uint8array).toV0().toString()
}

export async function loadIpfsFromCid(cid: string, ipfsUri = DEFAULT_IPFS_GATEWAY_URI): Promise<AppDataDoc> {
export async function loadIpfsFromCid(cid: string, ipfsUri = DEFAULT_IPFS_READ_URI): Promise<AppDataDoc> {
const { default: fetch } = await import('cross-fetch')
const response = await fetch(`${ipfsUri}/${cid}`)

Expand Down
51 changes: 51 additions & 0 deletions src/utils/context.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Context } from './context'
import { VoidSigner } from '@ethersproject/abstract-signer'
import { Provider } from '@ethersproject/providers'

test('Context: update chainId', async () => {
const context = new Context(1, {})
let chainId = await context.chainId
expect(chainId).toEqual(1)

// works
chainId = await context.updateChainId(4)
expect(chainId).toEqual(4)
})

test('Context: update chainId fails', () => {
const context = new Context(1, {})

expect(() => {
context.updateChainId(123)
}).toThrow('Invalid chainId: 123')
})

test('Context: get chainId from provider - matches context', async () => {
const mockProvider = jest.fn<Provider, []>()
const provider = new mockProvider()
provider.getNetwork = jest.fn(async () => ({ chainId: 1, name: 'bla' }))

const signer = new VoidSigner('', provider)

const context = new Context(1, { signer })

const chainId = await context.chainId

expect(chainId).toEqual(1)
expect(provider.getNetwork).toHaveBeenCalledTimes(1)
})

test('Context: get chainId from provider - differs from context', async () => {
const mockProvider = jest.fn<Provider, []>()
const provider = new mockProvider()
provider.getNetwork = jest.fn(async () => ({ chainId: 100, name: 'bla' }))

const signer = new VoidSigner('', provider)

const context = new Context(1, { signer })

const chainId = await context.chainId

expect(chainId).toEqual(100)
expect(provider.getNetwork).toHaveBeenCalledTimes(1)
})
16 changes: 13 additions & 3 deletions src/utils/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { Signer } from 'ethers'
import log from 'loglevel'
import { CowError, logPrefix } from './common'
import { SupportedChainId as ChainId } from '../constants/chains'
import { DEFAULT_APP_DATA_HASH, DEFAULT_IPFS_GATEWAY_URI } from '../constants'
import { DEFAULT_APP_DATA_HASH, DEFAULT_IPFS_READ_URI, DEFAULT_IPFS_WRITE_URI } from '../constants'

export interface Ipfs {
uri?: string
writeUri?: string
readUri?: string
pinataApiKey?: string
pinataApiSecret?: string
}
Expand All @@ -21,7 +23,8 @@ export const DefaultCowContext = {
appDataHash: DEFAULT_APP_DATA_HASH,
isDevEnvironment: false,
ipfs: {
uri: DEFAULT_IPFS_GATEWAY_URI,
readUri: DEFAULT_IPFS_READ_URI,
writeUri: DEFAULT_IPFS_WRITE_URI,
apiKey: undefined,
apiSecret: undefined,
},
Expand All @@ -40,7 +43,14 @@ export class Context implements Partial<CowContext> {

constructor(chainId: ChainId, context: CowContext) {
this.#chainId = this.updateChainId(chainId)
this.#context = { ...DefaultCowContext, ...context }
this.#context = {
...DefaultCowContext,
...context,
ipfs: {
...DefaultCowContext.ipfs,
...context.ipfs,
},
}
}

updateChainId(chainId: ChainId) {
Expand Down
7 changes: 4 additions & 3 deletions src/utils/ipfs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CowError } from './common'
import { Ipfs } from './context'
import { AppDataDoc } from '../api/metadata/types'
import { DEFAULT_IPFS_WRITE_URI } from '../constants'

type PinataPinResponse = {
IpfsHash: string
Expand All @@ -10,7 +11,7 @@ type PinataPinResponse = {

export async function pinJSONToIPFS(
file: unknown,
{ uri, pinataApiKey = '', pinataApiSecret = '' }: Ipfs
{ writeUri = DEFAULT_IPFS_WRITE_URI, pinataApiKey = '', pinataApiSecret = '' }: Ipfs
): Promise<PinataPinResponse> {
const { default: fetch } = await import('cross-fetch')

Expand All @@ -20,10 +21,10 @@ export async function pinJSONToIPFS(

const body = JSON.stringify({
pinataContent: file,
pinataMetadata: { name: 'appData-affiliate' },
pinataMetadata: { name: 'appData' },
})

const pinataUrl = `${uri}/pinning/pinJSONToIPFS`
const pinataUrl = `${writeUri}/pinning/pinJSONToIPFS`

const response = await fetch(pinataUrl, {
method: 'POST',
Expand Down

0 comments on commit b02ea4a

Please sign in to comment.