일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 컨트렉트 동일한 함수이름 호출
- ethers websocket
- 스마트 컨트렉트 함수이름 중복
- ethers
- 머신러닝기초
- git rebase
- 컨트렉트 배포 자동화
- ethers type
- chainlink 설명
- 오블완
- 러스트기초
- ethers typescript
- vue기초
- 체인의정석
- rust 기초
- Vue
- SBT표준
- Vue.js
- ambiguous function description
- 러스트 기초
- 스마트컨트렉트 함수이름 중복 호출
- 러스트 기초 학습
- nest.js설명
- 스마트컨트렉트 예약어 함수이름 중복
- 티스토리챌린지
- ethers v6
- 스마트컨트렉트테스트
- multicall
- 프록시배포구조
- 스마트컨트렉트프록시
- Today
- Total
체인의정석
UniswapV3 백엔드 구축하며, 틱 단위 계산에 사용한 다시 볼 것 같은 함수들 정리 (tickSpace 맞추기, 데이터 쌓는 지점 체크 & 동일 블록에서의 값 합산, Promise.all의 중첩 사용) 본문
UniswapV3 백엔드 구축하며, 틱 단위 계산에 사용한 다시 볼 것 같은 함수들 정리 (tickSpace 맞추기, 데이터 쌓는 지점 체크 & 동일 블록에서의 값 합산, Promise.all의 중첩 사용)
체인의정석 2023. 8. 24. 18:55tickSpace 맞추기
tick별로 계산할 때 모든 틱을 다 관리할 수 없기에 uniswap V3에서 tick Space 단위로 맞출 때 올림 내림 처리를 해주어야 한다.
각 풀마다 tick이 다르다보니 각 풀별 기본 tickSpace를 컨트렉트 콜로 불러와서 사용해주어야 한다.
function adjustedStartTick(tick, tickSpace) {
return Math.floor(tick / tickSpace) * tickSpace;
}
function adjustedEndTick(tick, tickSpace) {
return Math.ceil(tick / tickSpace) * tickSpace;
}
// Test
console.log(adjustedStartTick(14, 10)); // Outputs: 10
console.log(adjustedEndTick(14, 10)); // Outputs: 20
console.log(adjustedStartTick(1003242, 1000)); // Outputs: 1003000
console.log(adjustedEndTick(1003242, 1000)); // Outputs: 1004000
Promise.all의 중첩 사용
Promise.all 안에 다시 await가 있다면 그 안에 다시 Promise.all을 사용해야 한다.
하지만 Promise.all 을 2중으로 겹치는 것은 복잡해지고 에러의 위치파악을 어렵게 한다.
따라서 구조적으로 분리를 하는 것이 좋으며
//Before
//외부의 경우 Promise.all 을 사용하여서 모든 결과 값을 리턴하도록 함
//DB접근 시 발생한 에러를 잡기 위해 밖은 예외 처리로 한번 감싸주기
await Promise.all(Array.map(async (entity) => {
//내부적으로 DB접근 시에는 일반 map을 써서 비동기 함수 형태로 병렬 실행
//DB접근 시에 예외처리 해주고 에러 발생
await Promise.all(
insideArray.map(async (insideData) => {
return await this.queryFunction();
})
)
return true;
}))
따라서 위와 같이 짜인 코드를
아래와 같이 쓰고 예외처리 구문도 추가하는 식으로 코드를 바꾸었다.
//After
async function insidePromise(entity) {
//try catch 추가
await Promise.all(
insideArray.map(async (insideData) => {
return await this.queryFunction(insideData.A, insideData.B);
})
)
}
await Promise.all(Array.map(async (entity) => {
//try catch 추가
await insidePromise(entity)
return true;
}))
데이터 쌓는 지점 체크 & 동일 블록에서의 값 합산
블록번호를 지정하여 조건을 체크하는 방식으로 한번 저장된 블록이 다시 들어올 경우 저장되지 않도록 한다.
하나의 블록안에 여러 수치에 대한 값이 있기 때문에 업데이트나 입력 전에 블록별로 한번에 묶어서 계산해 주는 과정이 필요하다.
이런식으로 동일 블록에서의 값을 합살 할때는 따로 객체를 하나 만들어서 key값을 지정한 후에 업데이트 해주고 리턴 값으로 Objects.values를 통하여 줘서 객체를 다시 배열 형태로 만들고 키 값을 제거하는 식으로 묶어서 계산하고 값을 리턴해 줄 수 있다.
mergeVolumeByTick(2darray) {
// Flatten the 2D array into 1D
const flattenedData = [].concat(...2darray);
const object = {};
// Merge volumes by tick
flattenedData.forEach(item => {
if (object[item.tick]) {
if(object[item.tick].block_number < item.block_number) {
object[item.tick].block_number = item.block_number;
}
object[item.tick].volume += item.volume;
} else {
object[item.tick] = {
block_number: item.block_number,
tick: item.tick,
volume: item.volume
};
}
});
return Object.values(tickMap);
}
예를 들어 object라는 객체를 만들고 그 안에다가 key값을 tick으로 두고 tick에 따른 block_number와 volume을 더해준다면 위와 같이 각 틱을 키로 두고 계산하여 값을 더해가면서 블록번호를 업데이트 해 줄 수 있다.
그리고 결과 값에서 객체의 값들만 리턴하는 식으로하여서 틱으로 둔 키 값을 제거하고 원하는 형태로 병합된 데이터를 넘겨 줄 수 있다.
해당 데이터를 만든 후에 쿼리로 저장할 때 또한 쿼리에 필요한 값들을 다음과 같이 넘길 수 있다.
여기서는 키 값이 2개가 나오는데 이렇게 2개의 키 값을 통해서 병합을 하게 된다면 block_number와 volume 2개의 조건절이 있을때 insert할 값들 여러개를 뽑아서 가지고 있다가 한번에 저장시킬 수 있다.
mergeSameTickData(data) {
const result = data.reduce((acc, entity) => {
//중복을 체크하기 위하여 내부적으로 사용하는 key값
const key = `${data.block_number}-${data.volume}`;
if (!acc[key]) {
acc[key] = {
block_number: data.block_number,
volume: data.volume,
tick: []
};
}
acc[key].tick.push(entity.tick);
return acc;
}, {});
return Object.values(result);
}
reduce를 사용해서 기존에 acc안에 기존 값이 있다면 tick만 업데이트하고 기존 값이 없다면 최초 기록이므로 블록번호와 볼륨을 같이 넣어주면 된다.
이처럼 데이터 쌓는 지점을 정확히 체크하고 중복 업데이트를 막기 위해서 미리 블록번호당 데이터를 병합한 후에 넣어주고 데이터를 넣기 전에 데이터가 존재하는지 체크한다면 중복 적재 없이 db를 쌓을 수 있다.
배열 쪼개기
위와 같이 블록단위로 저장을 할 때 데이터가 많아서 배열 하나를 쪼개서 처리하고자 한다면 다음과 같은 함수를 사용할 수 있다.
splitIntoChunks(array, chunkSize) {
const results = [];
while (array.length) {
results.push(array.splice(0, chunkSize));
}
return results;
}
'개발 > backend' 카테고리의 다른 글
Nest.js 기본 구조 공부(기본 구조, Controller, Provider, Moudule) (1) | 2023.11.24 |
---|---|
Bignumber.js 사용하여 데이터 처리하기 (0) | 2023.08.25 |
javascript) 한 객체에 다수의 배열이 있을때 some 함수로 모두 검색하는 방법 (find vs indexOf vs some) (0) | 2023.08.07 |
map으로 객체안의 값 null 일 경우 0으로 바꾸기 (0) | 2023.07.24 |
node.js에서 API로 파일 다운 및 압축 파일 다운 받게 만들기 (블록체인 프로젝트 DB정합성 검사기 제작) (0) | 2023.05.17 |